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);
});
});
Related
I am trying to implement mocha testing, however it doesn't seem to work the way I expect it to. My code looks like the following:
async function getFoo() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved')
}, 2500)
})
}
describe(`Testing stuff`, function () {
it('resolves with foo', () => {
return getFoo().then(result => {
assert.equal(result, 'foo')
})
})
})
Error message:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
To me it seems like I have to increase the threshold of the timeout.
Is there a better way to do this? Since I don't know how long each test might take. I am testing nodejs child processes
best regards
As it is described in the error you need to use done() end of your test.
function delay(timeout = 2500) {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved')
}, timeout)
})
}
describe(`Testing stuff`, function (done) {
it('resolves with foo', (done) => {
return delay().then(result => {
assert.equal(result, 'foo')
done()
})
})
});
// better readability
describe(`Testing stuff`, function (done) {
it('resolves with foo', (done) => {
const result = await delay();
assert.equal(result, 'foo')
done();
})
});
But as you described you are testing child_processes which I don't recommend. Because to test child_process you need to promisify the child_process which doesn't make sense. Check here for more info How to promisify Node's child_process.exec and child_process.execFile functions with Bluebird?
If you really want to test child_process maybe you can mock the process with the link i gave but only for testing purposes.
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.
I'm using sinon to stub a function res.status, res.status is called in both unit test below, one pass and the other fail, I even debugged and went line by line and see function is being called yet sinon fails and say the function wasn't called.
this is controller that I'm testing (ActivityController.js)
exports.findActivity = (req, res) => {
ActivityService.findActivity(req.params.id, req.user).then(result => {
res.status(200).json({result: result})
}).catch(err => {
res.status(500).json({msg: err.message})
})
}
and here are the unit tests, the first test pass fine yet the second test doesn't
it('should return Activity obj with status 200', async () => {
expectedResult = activity
sinon.stub(ActivityService, 'findActivity').resolves(expectedResult)
await ActivityController.findActivity(req, res)
sinon.assert.calledWith(ActivityService.findActivity)
expect(res.status.calledOnce).to.equal(true)
sinon.assert.calledWith(res.status, 200)
})
it('should return status 500 on server error', async () => {
sinon.stub(ActivityService, 'findActivity').rejects()
await ActivityController.findActivity(req, res)
sinon.assert.calledWith(ActivityService.findActivity, req.params.id, req.user)
expect(res.status.calledOnce).to.equal(true)
sinon.assert.calledWith(res.status, 500)
})
I debuged the second unit test and see ActivityController go through catch and call res.status(500) yet the unit test fails in res.status.calledOnce
Are you reseting the res.status stub in between each test? res.calledOnce might not be true since it might be called more than once.
It seems that expect(res.status.calledOnce).to.equal(true) was called immediately before waiting for sinon.assert.calledWith.
putting await before sinon.assert fixed the problem.
so the correct code is
it('should return status 500 on server error', async () => {
sinon.stub(ActivityService, 'findActivity').rejects()
await ActivityController.findActivity(req, res)
await sinon.assert.calledWith(ActivityService.findActivity, req.params.id, req.user)
expect(res.status.calledOnce).to.equal(true)
sinon.assert.calledWith(res.status, 500)
})
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();
}
The following test is behaving oddly:
it('Should return the exchange rates for btc_ltc', function(done) {
var pair = 'btc_ltc';
shapeshift.getRate(pair)
.then(function(data){
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
done();
})
.catch(function(err){
//this should really be `.catch` for a failed request, but
//instead it looks like chai is picking this up when a test fails
done(err);
})
});
How should I properly handle a rejected promise (and test it)?
How should I properly handle a failed test (ie: expect(data.rate).to.have.length(400);?
Here is the implementation I'm testing:
var requestp = require('request-promise');
var shapeshift = module.exports = {};
var url = 'http://shapeshift.io';
shapeshift.getRate = function(pair){
return requestp({
url: url + '/rate/' + pair,
json: true
});
};
The easiest thing to do would be to use the built in promises support Mocha has in recent versions:
it('Should return the exchange rates for btc_ltc', function() { // no done
var pair = 'btc_ltc';
// note the return
return shapeshift.getRate(pair).then(function(data){
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
});// no catch, it'll figure it out since the promise is rejected
});
Or with modern Node and async/await:
it('Should return the exchange rates for btc_ltc', async () => { // no done
const pair = 'btc_ltc';
const data = await shapeshift.getRate(pair);
expect(data.pair).to.equal(pair);
expect(data.rate).to.have.length(400);
});
Since this approach is promises end to end it is easier to test and you won't have to think about the strange cases you're thinking about like the odd done() calls everywhere.
This is an advantage Mocha has over other libraries like Jasmine at the moment. You might also want to check Chai As Promised which would make it even easier (no .then) but personally I prefer the clarity and simplicity of the current version
As already pointed out here, the newer versions of Mocha are already Promise-aware. But since the OP asked specifically about Chai, it's only fair to point out the chai-as-promised package which provides a clean syntax for testing promises:
using chai-as-promised
Here's how you can use chai-as-promised to test both resolve and reject cases for a Promise:
var chai = require('chai');
var expect = chai.expect;
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
...
it('resolves as promised', function() {
return expect(Promise.resolve('woof')).to.eventually.equal('woof');
});
it('rejects as promised', function() {
return expect(Promise.reject('caw')).to.be.rejectedWith('caw');
});
without chai-as-promised
To make it really clear as to what's getting tested, here's the same example coded without chai-as-promised:
it('resolves as promised', function() {
return Promise.resolve("woof")
.then(function(m) { expect(m).to.equal('woof'); })
.catch(function(m) { throw new Error('was not supposed to fail'); })
;
});
it('rejects as promised', function() {
return Promise.reject("caw")
.then(function(m) { throw new Error('was not supposed to succeed'); })
.catch(function(m) { expect(m).to.equal('caw'); })
;
});
Here's my take:
using async/await
not needing extra chai modules
avoiding the catch issue, #TheCrazyProgrammer pointed out above
A delayed promise function, that fails, if given a delay of 0:
const timeoutPromise = (time) => {
return new Promise((resolve, reject) => {
if (time === 0)
reject({ 'message': 'invalid time 0' })
setTimeout(() => resolve('done', time))
})
}
// ↓ ↓ ↓
it('promise selftest', async () => {
// positive test
let r = await timeoutPromise(500)
assert.equal(r, 'done')
// negative test
try {
await timeoutPromise(0)
// a failing assert here is a bad idea, since it would lead into the catch clause…
} catch (err) {
// optional, check for specific error (or error.type, error. message to contain …)
assert.deepEqual(err, { 'message': 'invalid time 0' })
return // this is important
}
assert.isOk(false, 'timeOut must throw')
log('last')
})
Positive test is rather simple. Unexpected failure (simulate by 500→0) will fail the test automatically, as rejected promise escalates.
Negative test uses the try-catch-idea. However: 'complaining' about an undesired pass happens only after the catch clause (that way, it does not end up in the catch() clause, triggering further but misleading errors.
For this strategy to work, one must return the test from the catch clause. If you want't to test anything else, use another it()-block.
Thre is a better solution. Just return the error with done in a catch block.
// ...
it('fail', (done) => {
// any async call that will return a Promise
ajaxJson({})
.then((req) => {
expect(1).to.equal(11); //this will throw a error
done(); //this will resove the test if there is no error
}).catch((e) => {
done(e); //this will catch the thrown error
});
});
this test will fail with following message: AssertionError: expected 1 to equal 11