While testing with Mocha I am getting the following error on running server.test.js
1) "before each" hook for "should get all todos":
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
server.test.js
const expect = require('expect');
const request = require('supertest');
const {app} = require('./../server');
const {Todo} = require('./../todos');
const todos = [
{
text: 'This is text 1'
},
{
text: 'This is text 2'
}
];
beforeEach((done) => {
Todo.remove({}).then(() => {
return Todo.insertMany(todos);
}).then(() => done());
});
describe('GET /todos', () => {
it('should get all todos', (done) => {
request(app)
.get('/todos')
.expect(200)
.expect(res => {
expect(res.body.length).toBe(2);
})
.end(done);
});
});
But if I do some changes in beforeEach() method like:
updated server.test.js
const expect = require('expect');
const request = require('supertest');
const {app} = require('./../server');
const {Todo} = require('./../todos');
const todos = [
{
text: 'This is text 1'
},
{
text: 'This is text 2'
}
];
beforeEach((done) => {
Todo.remove({}).then(() => {
Todo.insertMany(todos);
done();
})
});
describe('GET /todos', () => {
it('should get all todos', (done) => {
request(app)
.get('/todos')
.expect(200)
.expect(
expect(res.body.length).toBe(2);
})
.end(done);
});
});
Then I am getting no errors. Basically, by chaining promises in beforeEach() method I am running into an error but without that everything is fine.
Could anyone explain why is it happening?
server.js
var express = require('express');
var body_parser = require('body-parser');
const {mongoose} = require('./mongoose.js');
const {Todo} = require('./todos');
const {Todo_1} = require('./todos');
var app = express();
app.use(body_parser.json());
// using GET method
app.get('/todos', (req, res) => {
Todo.find().then((todos) => {
res.send(todos);
}, (err) => {
res.status(400).send(err);
});
});
module.exports = {app}
app.listen(3000, () => {
console.log('Server is up on the port 3000');
})
This is incorrect way to handle promises:
Todo.remove({}).then(() => {
Todo.insertMany(todos);
done();
})
});
Todo.insertMany is likely asynchronous and returns a promise, and it's detached from promise chain. If there are errors, they will result in unhandled promise rejections, and since tests depend on inserted rows, this will result in race condition:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
means exactly what it says. done either wasn't called, or there was a timeout. It is possible for done to never be called because errors aren't handled. It should be .then(done, done).
Since Mocha supports promises, a proper way is:
beforeEach(() =>
Todo.remove({})
.then(() => Todo.insertMany(todos))
);
Every promise that appears somewhere in promise chain should be returned.
instead of putting localhost in the url like this:
"mongodb://localhost:27017/yourDbName"
use 127.0.0.1, so it becomes like the following:
"mongodb://127.0.0.1:27017/yourDbName"
I do not know what is the reason behind this solution, but it seems that the server needs some time to process and figure out what the IP of the localhost is.
Hopefully this solution solves your issue.
Related
i'm doing some integration tests in my API with mocha, chai, sinon and chai-http.
I made the tests for my post route and works fine, but, when I tried to do the same with my get route, the stub isn't workig. The code is bellow.
describe('Commentary API Test', () => {
describe('POST commentary', () => {
before(() => {
sinon.stub(CommentaryModel.prototype, 'add').resolves(newCommentaryResponse as any);
});
after(() => {
(CommentaryModel.prototype.add as SinonStub).restore();
});
it('should return status 201 and a new commentary json', async () => {
const response = await chai.request(app).post('/commentary').send(newCommentaryPayload)
expect(response).to.have.status(201);
expect(response.body).to.be.deep.equal(newCommentaryResponse);
});
it('should return error 400 with incorrect payload and a message', async () => {
const response = await chai.request(app).post('/commentary').send(newCommentaryPayload.pokemonName);
expect(response).to.have.status(400);
expect(response.body).to.be.deep.equal({"message":"All fields must be correctly filled"});
});
});
describe("GET commentaries", () => {
before(() => {
sinon.stub(CommentaryModel.prototype, 'get').resolves(1 as any);
});
after(() => {
(CommentaryModel.prototype.get as SinonStub).restore();
});
})
it('should return status 200 and a array with commentaries', async () => {
const response = await chai.request(app).get('/commentary');
console.log(response.body)
expect(response).to.have.status(200);
expect(response.body).to.be.equal(commentaryArray);
});
});
I put the number 1 as response, but i still getting a array with the pokemon infos... any idea why this stub isn't working?
Change the stub values some times.
After the upgrade, Mocha can not even run a simple test here is the code
const assert = require('assert');
it('should complete this test', function (done) {
return new Promise(function (resolve) {
assert.ok(true);
resolve();
})
.then(done);
});
I took this code from here
I understood that it now throws an exception Error: Resolution method is overspecified. Specify a callback * or * return a Promise; not both.
But how to make it work? I did not understand. I have
node -v 6.9.4
mocha -v 3.2.0
How to run this code are now in a new and correct format?
Just drop
.then(done); and replace function(done) with function()
You are returning a Promise so calling done is redundant as it said in error message
In the elder versions you had to use callback in case of async methods like that
it ('returns async', function(done) {
callAsync()
.then(function(result) {
assert.ok(result);
done();
});
})
Now you have an alternative of returning a Promise
it ('returns async', function() {
return new Promise(function (resolve) {
callAsync()
.then(function(result) {
assert.ok(result);
resolve();
});
});
})
But using both is misleading
(see for example here https://github.com/mochajs/mocha/issues/2407)
Mocha allows to either use a callback:
it('should complete this test', function (done) {
new Promise(function (resolve) {
assert.ok(true);
resolve();
})
.then(done);
});
OR return a promise:
it('should complete this test', function () {
return new Promise(function (resolve) {
assert.ok(true);
resolve();
});
});
// Or in the async manner
it('should complete this test', async () => {
await Promise.resolve();
assert.ok(true);
});
You can't do both.
I had to removed the done from the function parameter and the done() of the function call
Before
before(async function (done) {
user = new User({ ...});
await user.save();
done()
});
After
before(async function () {
user = new User({ ...});
await user.save();
});
These works for me
I had this same issue. A lot of times Mocha is paired with another library called Chai. Chai has a package called "chai-as-promised". It gives you the super simple ability to write less code and test promises. In your case of just testing if a promise resolves, it seems perfect.
const chai = require('chai');
const chaiAsPromised = require("chai-as-promised");
const should = require("chai").should();
chai.use(chaiAsPromised);
describe("Testing with correct syntax and non repeated names", () => {
it("Should give us a positive response", () => {
graphQL.sendToGQL(model,"specialEndpoint").should.eventually.be.an("Object");
})
})
An example of async functions with done breaking.
Failure Case
it('If the credentials exists in the system it should return the token generated against it.', async (done) => {
let aObj = await admin.createAdmin();
chai.request(server)
.post("/authenticate")
.set("Content-Type", "application/x-www-form-urlencoded")
.send({username: aObj.login,password:aObj.password})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("string");
done();
});
});
Success Case
it('If the credentials exists in the system it should return the token generated against it.', async () => {
let adminObj = await admin.createAdmin();
chai.request(server)
.post("/auth/login")
.set("Content-Type", "application/x-www-form-urlencoded")
.send({username: adminObj.login,password:adminObj.password})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("string");
// done();
});
});
If you don't have callbacks, prior answers (which suggest deleting the done) is correct.
If need to both await some external promise, and then exercise a callback/errback-based implementation in your test, that solution doesn't help you.
You can use a library like pify to convert the callback API to use promises.
Alternatively, you can use a Latch in your callback:
it("test", async () => {
const l = new Latch()
const v = await promiseValue()
s.methodThatTakesCallback((err, result) => {
expect(result).to.eql(expected)
l.resolve() // < notifies mocha your test is done
})
return l.promise
})
In TypeScript, here's a very stripped-down Latch implementation:
/**
* Simple one-count concurrent barrier
*/
export class Latch {
readonly promise: Promise<void>
resolve!: () => void
constructor() {
this.promise = new Promise<void>(resolve => (this.resolve = resolve))
}
}
Just emit done callback completely and use async instead.
(This implementation is based on an express api running on firebase functions, using a custom jsonwebtoken)
const { FIREBASE_UID } = require('dotenv').config()?.parsed
const chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../lib/api').API;
const should = chai.should();
const expect = chai.expect
chai.use(chaiHttp)
const test = chai.request(server).keepOpen()
// get your token with an earlier mock request and store to a var
describe('Just checking a token', () => {
let some_token
it('should print custom jwt for testing, status: 200'), async () => {
try {
const res = await test.get(`/createCustomFirebaseToken/${FIREBASE_UID}`).send()
res.should.exist
res.should.have.status(200);
res.should.have.json
some_token = (JSON.parse(res.text)).token
} catch (error) {
throw error
}
}
it('should print details:PING, status:200'), async () => {
try {
const res = await test.get('/').set('Authorization',`Bearer ${some_token}`)
.send()
res.should.exist
res.should.have.status(200);
res.should.have.json
const { details, status } = JSON.parse(res.text)
expect(details).to.equal('PING')
expect(status).to.equal(200)
} catch (error) {
throw error
}
}
after(() => test.close())
})
I have been trying to write a test suite to my node.js API project and one of their requirements is to control when the server starts and stops. For that, I wrote this code below with two functions: initializeWebServer and stopWebServer.
express.js
const initializeWebServer = () => {
return new Promise((resolve, reject) => {
app = express();
/* some middlewares */
app.use('/', userRoutes);
httpServer = http.createServer(app);
httpServer.listen(3000, (err) => {
if (err) {
reject(err);
return;
}
resolve(app);
});
});
};
const stopWebServer = () => {
return new Promise((resolve, reject) => {
httpServer.close(() => { resolve(); });
});
};
Using mocha to run my tests, I choose to manage my server connection with before and after hooks, using async/await syntax.
user.spec.js
let axiosAPIClient;
before(async () => {
await initializeWebServer();
const axiosConfig = {
baseURL: `http://localhost:3000`,
validateStatus: () => true
};
axiosAPIClient = axios.create(axiosConfig);
});
after(async () => {
await stopWebServer();
});
describe('POST /api/user', () => {
it('when add a new user, then should get back approval with 200 response', async () => {
const userData = {
/* user props */
};
const response = await axiosAPIClient.post('/api/user', userData);
expect(response).to.containSubset({
status: 200,
data: { message: 'User signed up.' }
});
When axios (I tried fetch too) submit any HTTP request, mocha returns the following error: Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.. I tried to increase the timeout interval, but it didn't work.
If I execute my test suite without hooks, initializing my server with nodemon pointing to my index file with the same await initializeWebServer();, HTTP requests work as it should.
I really don't understand why this is happening, I think it's something with mocha.
I did some debug tests and figured out that I forgot to set up a db connection when I run my test suite, including in before and after hooks. It was a simple mistake.
user.spec.js
let axiosAPIClient;
before(async () => {
await initializeWebServer();
db.setUp();
/* axios config etc. */
after(async () => {
await stopWebServer();
await db.closeConnection();
});
I'm doing a POST to create an item and send the newly created item as response back to the client.
async (req, res, next) => {
const item = await createItem(xx, yy, zz);
res.send(201, item);
}
Now I also want to send out notifications after creating an item but also after responding to the client - to make the request as fast as possible.
async (req, res, next) => {
const item = await createItem(xx, yy, zz);
res.send(201, item);
sendNotification(item);
}
If I want to test this using jest + supertest, this is how it'd look:
test('return 201', () => {
const app = require('./');
return request(app)
.post('/api/items')
.send({})
.expect(201)
.then(response => {
// test something more
});
}
But how could I test if the sendNotification() was called?
Ok, not perfect but working right now:
I added a call to an external method from another package at the end of the async request-handler. I know that you shouldn't add code just for testing purposes but I prefered this to random setTimeouts in my tests.
hooks.js
const deferreds = [];
exports.hookIntoEnd = () => {
const p = new Promise((resolve, reject) => {
deferreds.push({ resolve, reject });
});
return p;
};
exports.triggerEndHook = () => {
if (Array.isArray(deferreds)) {
deferreds.forEach(d => d.resolve());
}
};
handler.js
const { triggerEndHook } = require('./hooks');
async (req, res, next) => {
const item = await createItem(xx, yy, zz);
res.send(201, item);
sendNotification(item);
// this is only here so that we can hook into here from our tests
triggerEndHook();
}
test.js
test('run + test stuff after res.send', async () => {
const server = require('../index');
const { hookIntoEnd } = require('../hooks');
const aws = require('../utils/aws');
const getObjectMetadataSpy = jest
.spyOn(aws, 'getObjectMetadata')
.mockImplementation(() => Promise.resolve({ Metadata: { a: 'b' } }));
const p = hookIntoEnd();
const response = await request(server)
.post('/api/items')
.send({ foo: 'bar' })
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(201);
expect(response.body).toEqual({ id: 1, name: 'test item'});
// test for code that was run after res.send
return p.then(async () => {
console.log('>>>>>>>>>>> triggerEndHook');
expect(getObjectMetadataSpy).toHaveBeenCalledTimes(2);
});
});
You can use mocking in Jest to spy on the sendNotification() function and assert that it has been called. A simple example:
const sendNotification = require('./sendNotification');
const sendNotificationSpy = jest.spyOn(sendNotification);
test('return 201', () => {
const app = require('./');
return request(app)
.post('/api/items')
.send({})
.expect(201)
.then(response => {
// test something more
expect(sendNotificationSpy).toHaveBeenCalled();
});
}
After res.send() is called the program calls someService.method({param1}) function.
Using sinon to spy that service method:
it('test after send', function(done){
const spy = sinon.spy(someService, 'method');
agent
.set('Authorization', token)
.post('/foo')
.expect(200)
.then(() => {
return setTimeout(function() {
// Assert the method was called once
sinon.assert.callCount(spy, 1);
// Assert the method was called with '{param1}' parameter
sinon.assert.calledWith(spy, {param1});
// Test callback!
done();
}, 100);
});
});
- Using setTimeout with the minimus time (ms) as possible to wait for the method to be called.
Recommendations and improvements will be appreciated! (I'm still trying to avoid using arbitrary amount of timeout)
Why does the following code fail with a timeout? It looks like 'should' throws an error and done() never gets called? How do I write this test so that it fails correctly instead of having jasmine reporting a timeout?
var Promise = require('bluebird');
var should = require('chai').should();
describe('test', function () {
it('should work', function (done) {
Promise.resolve(3)
.then(function (num) {
num.should.equal(4);
done();
});
});
});
console output is:
c:>jasmine-node spec\
Unhandled rejection AssertionError: expected 3 to equal 4
...
Failures:
1) test should work
Message:
timeout: timed out after 5000 msec waiting for spec to complete
Using .then() and only done()
it('should work', (done) => {
Promise.resolve(3).then((num) => {
// your assertions here
}).catch((err) => {
expect(err).toBeFalsy();
}).then(done);
});
Using .then() and done.fail()
it('should work', (done) => {
Promise.resolve(3).then((num) => {
// your assertions here
}).then(done).catch(done.fail);
});
Using Bluebird coroutines
it('should work', (done) => {
Promise.coroutine(function *g() {
let num = yield Promise.resolve(3);
// your assertions here
})().then(done).catch(done.fail);
});
Using async/await
it('should work', async (done) => {
try {
let num = await Promise.resolve(3);
// your assertions here
done();
} catch (err) {
done.fail(err);
}
});
Using async/await with .catch()
it('should work', (done) => {
(async () => {
let num = await Promise.resolve(3);
// your assertions here
done();
})().catch(done.fail);
});
Other options
You specifically asked about jasmine-node so that's what the above examples are about but there are also other modules that let you directly return promises from tests instead of calling done() and done.fail() - see:
https://www.npmjs.com/package/jasmine-promises
https://www.npmjs.com/package/jasmine-as-promised
https://www.npmjs.com/package/jasmine-promise-matchers
https://www.npmjs.com/package/jasmine-then
If you want to use Promises in your Mocha suite, you have to return it instead of using the done() callback, like:
describe('test', function () {
it('should work', function () {
return Promise.resolve(3)
.then(function (num) {
num.should.equal(4);
});
});
});
An even cleaner way to write that would be with chai-as-promised module:
describe('test', function () {
it('should work', function () {
return Promise.resolve(3).should.eventually.equal(4);
});
});
Just make sure to require it properly and tell chai to use it:
var Promise = require('bluebird');
var chai = require('chai');
var should = chai.should();
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);