I have following method:
EventHandler.prototype.handle = function (event) {
var me = this;
return me.processEvent(event).then(function () {
return me.saveLastSeenEventRevision(event);
}).catch(function (err) {
me.logger.debug("First attempt of event process failed, trying recovery");
return me.recoveryStrategy.execute(event,me.processEvent.bind(me)).then(function() {
return me.saveLastSeenEventRevision(event);
});
});
};
And I have this test written:
describe('when processEvent fails', function() {
beforeEach(function () {
instance.processEvent.returns(Bluebird.reject(new Error('rejection')));
});
describe('when recovery fails', function() {
beforeEach(function () {
instance.recoveryStrategy.execute.returns(Bluebird.reject(new Error('recovery rejected')));
});
it('should not save the revision', function(done) {
instance.handle(event).catch(function() {
sinon.assert.notCalled(instance.saveLastSeenEventRevision);
done();
}).done();
});
});
});
I simulate rejection of processEvent method, and in catching function I simulate final rejection of my recovery implementation. I have more test written for this method, but the setup is the same. All test passes as they are supposed to, but in console I see this message:
Possibly unhandled Error: rejection
Is there any other way of handling these promises to pass this test?
Test setup - mocha / chai / sinon, as promise library is used bluebird.
Well, you're creating an explicit rejection but are not handling it here:
instance.processEvent.returns(Bluebird.reject(new Error('rejection')));
Bluebird sees you're creating an unhandled rejection here so it's letting you know. This is because a real function would return the rejection when called but sinon is creating it early - so the app has a pending place of error that is not handled. You can turn that off with:
Bluebird.onPossiblyUnhandledRejection(function(){});
Or better, use a tool like sinon-as-promised which lets you do:
instance.processEvent.rejects('rejection');
On a side note - don't use the silly done syntax, Mocha has built in promises support. You can return promises from test.
So, problem was with sinon stubs and setting up return values. It seems, that passing rejected promise as return value is some how evaluated inside sinon itself.
There are two options:
to not set return value directly, but implement function which will be used instead of the stub one
sinon.stub(instance, 'processEvent', function() {
return Bluebird.reject(new Error('rejected');
});
to use sinon-as-promised library, which allows following:
sinon.stub(instance, 'processEvent').rejects(new Error('rejected'));
Related
I'm using supertest, chai and mocha to test my Web API application. I have the following code:
it('should return 500', function(done) {
this.timeout(30000);
request(server)
.get('/some/path')
.expect(500)
.end(function(err, res) {
done();
});
});
It should fail. The code which runs in the get request is:
// Inside method getData
try {
// Get data
// throws error
} catch (e) {
// catches error and displays it
deferred.reject(e);
return deferred.promise;
}
// Other code not in getData method
dbOps.params.getData(collection, parameter, query).then(
function (arr) {
response.send(arr);
}, function (err) {
logger.error(err.message);
response.status(500).send(err.message);
}
);
It basically does deferred.reject(e); and sends the error as the response of the API. I would like to catch the deferred.reject(e); part and in the same time continue the chain of .except(500).end(...). Something like:
catch_deferred(request(server).get('/some/path'))
.expect(500)
.end(function(err, res) {
expect(err).to.equal(null);
expect(res.body).to.be.an('object').that.is.empty;
done();
Is there some way to do it? I can't use the try-catch block because its not an exception. Also I can't chai's expect().to.throw() because there is not exception being thrown.
Disclaimer: I never use deferred.reject().
Possible solution: use sinon.
You can use spy, if you want the real deferred.reject() to run and you just want to monitor it. Monitor means: to know whether the method get called, with what argument, and also the return value. Example:
// Preparation: (Disclaimer: I never use Deferred.reject())
const spyDeferredReject = sinon.spy(Deferred, 'reject');
// Do stuff..
// Expectation phase: check whether deferred reject get called.
expect(spyDeferredReject.calledOnce).to.equal(true);
// Check whether deferred reject called with correct argument.
expect(spyDeferredReject.args[0][0]).to.be.an('error');
You can use stub, if you want to monitor it and make sure the real method not get called.
Hope this helps.
After upgrading to angular 1.6.1 I've faced an issue with promise handling.
Imagine I have following function inside a component:
function action(arg) {
service
.doRequest(vm.userId, arg)
.finally(function(){
vm.called = true;
});
}
Then I have to test success and error path to make sure my vm.called property has been set in both cases.
Here the tests:
describe('when succeeds', function () {
beforeEach(function () {
spyOn(service, 'doRequest').and.returnValue($q.resolve())
subject = createComponent();
subject.action('test arg');
$scope.$digest();
});
it('should set called property to true', function () {
expect(subject.called).toBeTruthy();
});
});
describe('when fails', function () {
beforeEach(function () {
spyOn(service, 'doRequest').and.returnValue($q.reject())
subject = createComponent();
subject.action('test arg');
$scope.$digest();
});
it('should set called property to true even if something went wrong', function () {
expect(subject.called).toBeTruthy();
});
});
When testing the false path, unhandled rejection error is thrown due to this change and because I'm not catching any exceptions.
I also know that it can be fixed by:
Configuring $qProvider.errorOnUnhandledRejections(false); for test module
Adding .catch(angular.noop) handler into the subject code to explicitly catch unhandled rejection.
modifying original code to return the promise from the service.doRequest() call and adding .catch(angular.noop) handler into the test itself when calling subject.action('test arg');.
Option 1 and 2 aren't acceptable because they will swallow all error which might occur.
Option 3 works for this particular case but will not work if the function already has a return value.
So my question: Is there any way to ignore any errors in the promise chain but avoiding "unhandled rejection" errors in tests without using 1-3 options?
Thanks in advance.
The below code all functions correctly with the exception of the assertion. When the test is run Jasmine reports 0 assertions. How can I keep my assertions within the promises and have them be recognized?
it("should open save NTP server modal", function (done) {
var addModal = driver.findElement(By.className('modal-dialog'));
driver.wait(until.stalenessOf(addModal), 5000).then(function () {
return driver.wait(until.elementIsEnabled(driver.findElement(By.id('saveButton'))), 5000).then(function (element){
return element.click();
});
});
driver.findElement(By.className("modal-body")).then(function (element) {
return expect(element.isDisplayed()).toBeTruthy();
});
done();
});
I know that in this specific case(my best example test) I could just catch the element and then do the expect outside of a promise:
var element = driver.findElement(By.className("modal-body"));
expect(element.isDisplayed()).toBeTruthy();
Unfortunately I have other cases where I cannot determine a way to do the exception outside of a promise.
You have to put your "done" method inside the final callback.
driver.findElement(By.className("modal-body")).then(function (element) {
expect(element.isDisplayed()).toBeTruthy();
done();
});
You can also look into Chai promises library, which is meant for handling async expects.
I want to get the result of promise in before
describe('unsubscribe', function() {
var arn;
this.timeout(10000);
before(function(done) {
sns.subscribe('param1', 'param2').then(
(result) => {
arn = result;
done();
},
(error) => {
assert.fail(error);
done();
});
});
it('("param") => promise returns object', function() {
const result = sns.unsubscribe(arn);
expect(result).to.eventually.be.an('object');
});
});
Similarly, in after I want to get result of promise in test
describe('subscribe', function() {
var arn;
this.timeout(10000);
it('("param1","param2") => promise returns string', function(done) {
sns.subscribe('param1', 'param2').then(
(result) => {
arn = result;
expect(result).to.be.a('string');
},
(error) => {
assert.fail(error);
done();
});
});
after(function(done) {
sns.unsubscribe(arn).then(
(result) => done());
});
});
Is this code properly written? Is there any better practice? What is the recommended way to do so?
Every place you want to have Mocha wait for a promise to be resolved you should just return the promise rather than use done. So your before hook can be simplified to:
before(() => sns.subscribe('param1', 'param2')
.then((result) => arn = result));
This is much more readable than having done here and there and having to do anything special for error conditions. If there is an error, the promise will reject and Mocha will catch it and report an error. There's no need to perform your own assertions.
You have a test and an after hook that could also be simplified by just returning the promises they use rather than using done. And if you test depends on a promise, remember to return it. You've forgotten it in one of your tests:
it('("param") => promise returns object', function() {
const result = sns.unsubscribe(arn);
// You need the return on this next line:
return expect(result).to.eventually.be.an('object');
});
Tip: if you have a test suite in which all tests are asynchronous, you can use the --async-only option. It will make Mocha require all tests to call done or return a promise and can help catch cases where you forget to return a promise. (Otherwise, such cases are hard to if they don't raise any error.)
Defining a variable in the callback to describe and setting it in one of the before/beforeEach hooks and checking it in the after/afterEach hooks is the standard way to pass data between hooks and tests. Mocha does not offer a special facility for it. So, yes, if you need to pass data which is the result of a promise you need to perform an assignment in .then like you do. You may run into examples where people instead of using a variable defined in the describe callback will set fields on this. Oh, this works but IMO it is brittle. A super simple example: if you set this.timeout to record some number meaningful only to your test, then you've overwritten the function that Mocha provides for changing its timeouts. You could use another name that does not clash now but will clash when a new version of Mocha is released.
I'm doing some unit tests using Node.js and I want to fail a test like this:
doSomething()
.then(...)
.catch(ex => {
// I want to make sure the test fails here
});
I'm using Assert, so I found Assert.Fails. The problem is that fails expects actual and expected, which I don't have. The Node documentation doesn't say anything about them being required, but the Chai documentation, which is Node compliant, say they are.
How should I fail a test on the catch of a promise?
You can use a dedicated spies library, like Sinon, or you can implement a simple spy yourself.
function Spy(f) {
const self = function() {
self.called = true;
};
self.called = false;
return self;
}
The spy is just a wrapper function which records data about how the function has been called.
const catchHandler = ex => ...;
const catchSpy = Spy(catchHandler);
doSomething()
.then(...)
.catch(catchSpy)
.finally(() => {
assert.ok(catchSpy.called === false);
});
The basic principle is to spy on the catch callback, then use the finally clause of your promise to make sure that the spy hasn't been called.
If you will use mocha, then elegant way would be as following:
describe('Test', () => {
it('first', (done) => {
doSomething()
.then(...)
.catch(done)
})
})
If your Promise fail, done method will be invoked with the thrown exception as a parameter, so code above is equivalent to
catch(ex => done(ex))
In mocha invoking done() with the parameter fails the test.
Have you considered Assert.Ok(false, message)? It's more terse.
Assert.fail is looking to do a comparison and display additional info.