Test whether a promise has been rejected? - jestjs

I have seem many examples of how to test if a promise is rejected with a certain reason, like below:
it('should reject if no startdate is given', () => {
expect.assertions(1);
return expect(MyService.fetch()).rejects.toEqual({
error: 'Your code message',
});
});
But how do I write if I just want to test that a promise has been rejected regardless of reason.

Try to use .toBeDefined() instead of .toEqual(...)
EDIT:
You could also use .toThrow(), the method which is actually recommended by JEST
As I found later you needed to use async/await to test with this
Here is an example on how to test with them:
it('should test promise reject', async () => {
await expect(toReject(true)).rejects.toThrow()
});
Test the code here

If you expect a promise to be rejected use the .rejects.
test('should reject if no startdate is given', () => {
return expect(MyService.fetch()).rejects.toMatch('error');
});
You can combine async and await with .resolves or .rejects.
test('should reject if no startdate is given', async () => {
await expect(MyService.fetch()).rejects.toThrow('error');
});

Related

Mocha test with SuperTest always passes (even when wrong)

I'm using Mocha and SuperTest to test my Express API. However my first test always seems to pass when inside the .then() of my request().
I'm passing in a String to a test that is expecting an Array. So should definitely fail the test.
It fails outside of the then() as expected, but I won't have access to the res.body there to perform my tests.
Here is my code:
const expect = require('chai').expect;
const request = require('supertest');
const router = require('../../routes/api/playlist.route');
const app = require('../../app');
describe('Playlist Route', function() {
// before((done) => {
// }
describe('Get all playlists by user', function() {
it('Should error out with "No playlists found" if there are no Playlists', function() {
request(app).get('/api/playlists/all')
.then(res => {
const { body } = res;
// Test passes if expect here
expect('sdfb').to.be.an('array');
})
.catch(err => {
console.log('err: ', err);
});
// Test fails if expect here
expect('sdfb').to.be.an('array');
})
})
});
I found this article but I'm not using a try catch block, but I thought maybe it could have something to do with the promise.
Quick reponse
it('decription', function(done) {
asyncFunc()
.then(() => {
expect(something).to.be(somethingElse)
done()
})
})
Detailed response in the comment of #jonrsharpe
Rather than using done, simply return request(app).get('/api/playlists/all') since request() returns a promise. Since you have expect('sdfb').to.be.an('array'); twice, remove the one that's not in the .then callback. When using asynchronous code, remember that synchronous code that appears to come after the async chain will execute before the promise .then handlers. This is counterintuitive.
Here's the .then approach:
it('should ...', () => {
return request(app)
.get('/api/playlists/all')
.then(res => {
const {body} = res;
// assert here
});
});
The other approach is to await the promise yourself in the test case function, then make assertions on the resolved response object. In this case, drop the then chain. This approach is generally preferred as it reduces nesting.
it('should ...', async () => {
const res = await request(app).get('/api/playlists/all');
const {body} = res;
// assert here
});
If you don't let Mocha know you're working with asynchronous code by returning a promise, awaiting the promises, or adding and calling the done parameter, the assertions occur asynchronously after the test is over and disappear into the void, creating a false positive.
Skip .catch either way. Since you've informed Mocha of the promise, if it rejects, it'll let you know.

Mocha test cases executes before promise gets the data

Test cases(Test1, Test2) execute before promise get the data. This is the file mockExecution.js
describe('AC 1: This is suite one', ()=>
{
before((done)=>
{
promiseResp.then((data) => {
console.log("i am in the promise");
responseData = data;
process.exit(0);
}, (err) => {
console.log('promiseResp.err', err);
process.exit(1);
})
done();
})
it('Test1', (done)=>
{
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
done();
});
it('Test2', (done)=>
{
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
done();
});
});
PromiseResp inside the Before block doesn't execute. Therefore "responseData" variable doesn't have data and it throws test case failed. I guess there is some asynchronous time issue, but don't know how to resolve it and also where do i put this "process.exit(0)". Below is the actual output:
AC 1: This is suite one
I am in the before
1) Test1
2) Test2
0 passing (7ms)
2 failing
1) AC 1: This is suite one
Test1:
TypeError: Cannot read property 'measure' of undefined
at Context.it (QA/mockExecution.js:160:29)
2) AC 1: This is suite one
Test2:
TypeError: Cannot read property 'measure' of undefined
at Context.it (QA/mockExecution.js:167:29)
[process business logic and prints some logs here, i can't paste here]
finished analyzing all records
i am in the promise
npm ERR! Test failed. See above for more details.
I am expecting output in the following sequence:
[process business logic and prints some logs here, I can't paste here]
finished analyzing all records
AC 1: This is suite one
I am in the before
I am in the promise
1) Test1 passed
2) Test2 paseed
You need to call done within your then & after you actually
assigned responseData = data:
before((done) => {
promiseResp.then((data) => {
responseData = data;
// Promise has resolved. Calling `done` to proceed to the `it` tests.
done();
})
.catch((err) => {
// Calling `done` with a truthy `err` argument, in case
// the promise fails/rejects, to fail-early the test suite.
done(err);
})
})
otherwise before ends prematurely and proceeds to the next tests, before the promise actually resolves and assigns your responseData variable.
Here's a working example using the before hook:
const expect = require('chai').expect
const getFooValue = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve('foo')
}, 500)
})
}
describe('#getFooValue()', function () {
let responseData
before(done => {
getFooValue().then(data => {
responseData = data
done()
})
.catch(err => {
done(err)
})
})
it('response has a value of foo', () => {
expect(responseData).to.equal('foo');
})
it('response is a String', () => {
expect(responseData).to.be.a('String');
})
})
What you're doing now is:
You define the Promise.
You (prematurely) call done and Mocha proceeds to execute the it tests.
The it tests run while responseData is still undefined.
The Promise within before eventually resolves and assigns the responseData variable.
...but at that point it's too late. The tests have already run.
The use of done together with promises is an antipattern because this often results in incorrect control flow, like in this case. All major testing frameworks already support promises, including Mocha.
If default timeout (2 seconds) is not big enough for a promise to resolve, timeout value should be increased, e.g. as explained in this answer by setting it for current test suite (this in describe context). Notice that arrow function should be replaced with regular function to reach suite context.
It should be:
describe('AC 1: This is suite one', function () {
this.timeout(60000);
before(() => {
return promiseResp.then((data) => {
responseData = data;
});
});
it('Test1', () => {
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
});
...
No need for catch for a promise; promise rejections will be handled by the framework. No need for done in tests; they are synchronous.
There's no promise in your it(), so no reason for done(), but it should be called inside then() as it is a callback.
And overall it's cleaner to use async/await. It doesn't work well in before() though.
Also 'function()' is preferable in describe() to set timeout for the tests (Invoking it as a chained method never worked on my experience)
describe('AC 1: This is suite one', function() {
this.timeout(12000); //12 sec timeout for each it()
before((done) => {
promiseResp().then((data) => {
responseData = data;
done();
})
})
it('Test1', () => {
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
});
it('Test2', () => {
expect(responseData.measure.abc).not.toBe(responseData.measure_list.abc);
});
});

Catching thrown errors with SinonJS, UnhandledPromiseRejectionWarning

I have the following async function that checks the returned value from a promise and I having trouble writing
async function fetchData(pageLocation) {
const data = await
apiService.fetchPage(pageLocation);
if (!data || !data.mapping) {
const error = new Error(`Unknown channel ${pageLocation}`);
error.code = 404;
throw (error);
}
return data.mapping;
}
Test case
describe.only('fetchData', () => {
let fetchPage;
beforeEach(() => {
fetchPage =
sinon.stub().returns(Promise.resolve(mockMapping));
csfPageService.__set__({
apiService: {
fetchPage,
},
});
});
it('should throw an error when there is no available Data', () => {
channeData', async function() {
const fetchChannelSectionData = pageService.__get__('fetchData');
expect(async () => { await fetchData('pageLocation'); }).to.throw();
expect(fetchPage).to.be.calledWith('pageLocation');
console.log('----------------------2');
});
What causing the main issue is having an async function and a promise I am able to use the same approach when it is not an async function and there is no await I have looked into the following links
Catching thrown errors with SinonJS
https://www.chaijs.com/api/bdd/#method_throw
enter link description here
but I haven't been successful
please advise on how should this be done ...
That is one of the reasons I don't like async, await, they are just syntactic sugar over promises, but they uses normal/sync semantics but just in appearance.
Async functions never throws, no matter how bad is the error you throw inside it, they will just return a rejected promise. In your case, your function is not throwing at all, it is returning a rejected promise, and you are not attaching any catch hanlder to that promise, hence the warning. When you use async function or promises, forget about normal handling of errors, promises catches any error automatically and encapsulates them on a rejected promise.
So, in your case the correc way of doing this will vary depending on your testing framework, but it could be something like this:
it('should throw an error when there is no available Data', () => {
channeData', async function() {
const fetchChannelSectionData = pageService.__get__('fetchData');
fetchData('pageLocation').catch(err => {
expect(err).to.be.an.error();
expect(fetchPage).to.be.calledWith('pageLocation');
console.log('----------------------2');
})
});

Sinon.JS stub that resolves a promise returns "{}"

I have a Node.js function that returns a promise. I am using Sinon.JS stubs to resolve the promise. My console.log statements in the code show that the stub is working. However, what is returned is {} instead of what the Promise resolves to.
I reviewed these other SO posts, but neither were exactly the problem I am running into:
Sinon.JS stub a function that resolves a promise
When to reject/resolve a promise
Here is the function:
function publishMessage(pubsub, topicName, data) {
const topic = pubsub.topic(topicName);
const publisher = topic.publisher();
return publisher.publish(data)
.then((results) => {
const messageId = results[0];
return messageId;
})
.catch((error) => {
console.log('Error ', error);
return error;
}); };
Here is the test:
describe('publishMessage', function() {
describe('Success', function() {
it('should return the messageId', function(done) {
var publishMessage = index.__get__('publishMessage');
var promise = sinon.stub().resolves(['1111']);
var publisher = {
publish: promise
};
var topic = {
publisher: sinon.stub().returns(publisher)
};
var pubsub = {
topic: sinon.stub().returns(topic)
};
assert.equal('1111', publishMessage(pubsub, 'st', 'ds'));
assert.isTrue(topic.publisher.calledWith());
done();
});
});
});
And when I execute the test, the output from the console.log shows the resolve value is printed:
publishMessage
Success
1) should return the messageId
1111
0 passing (256ms)
1 failing
1) publishMessage
Success
should return the messageId:
AssertionError: expected '1111' to equal {}
at Context.<anonymous> (test/index.spec.js:63:14)
There's a few potential problem areas that I noticed.
First, I don't see where index is defined, so I can't confirm whether or not the function you expect is being returned from index.__get__('publishMessage');. You can confirm that the right function is returned by visually inspecting the result of
publishMessage.toString();
The other problem I see (and more likely the cause of your problem) is that you are returning a Promise from publishMessage(), but comparing the result of a call to that function to the value to which the Promise will eventually resolve. In other words, your comparing a Promise to a String. Unless your assertion library waits for the Promise to resolve before checking the result (similar to Jasmine), you are comparing a String to a Promise. To remedy this, simply wait for the Promise to resolve:
it('should return the messageId', function(done) {
// Set up the test case by defining publishMessage, etc.
publishMessage(pubsub, 'st', 'ds').then((result) => {
assert.equal(result, '1111');
assert.isTrue(topic.publisher.calledWith());
done();
}).catch(done);
}
Notice I added a .catch() on the Promise. This makes sure that any errors thrown in the Promise will show the appropriate error as opposed to just a timed out error.
If you're using a testing framework like Mocha or Karma/Jasmine, you can improve this a little more by directly returning the Promise instead of using done(). In my experience, returning the Promise results in much better stack traces and more helpful and accurate error messages when trying to debug a test case that uses Promises. As an example:
it('should return the messageId', function() {
// Set up the test case by defining publishMessage, etc.
return publishMessage(pubsub, 'st', 'ds').then((result) => {
assert.equal(result, '1111');
assert.isTrue(topic.publisher.calledWith());
});
}
Notice that I don't accept an argument in the test case anymore. In Mocha and Karma, this is how the framework determines how to treat the test case.
You don't wait for your promise to be resolved.
Try
publishMessage(pubsub, 'st', 'ds').then(result => {
assert.equal('1111', result);
assert.isTrue(topic.publisher.calledWith());
done();
}

How to use nockback with promises?

I am trying to use nockBack to automate my fixtures recording/playback with code that uses promises and chai-as-promised.
But I can't figure out how to make the async nockBack work with it.
context('sad path', () => {
it('should reject the promise', () => {
return nockBack('myfixture.json')
.then(() => assert.isRejected(myPromise.doIt(), Error));
});
});
This fails with a timeout because no callback is ever called.
I also tried with const nockBack = Promise.promisifyAll(require('nock').back), but it still times out.
Any thoughts on how to use nockBack with promises and chai-as-promised?

Resources