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);
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 a chai test that works without async/await syntax, but crashes with it. How to fix it?
The error is:
error Error [ERR_SERVER_ALREADY_LISTEN]: Listen method has been called more than once without closing.
code: 'ERR_SERVER_ALREADY_LISTEN'
Here is the same test with a working classic syntax and a non-working async/await syntax:
chai.use(chaiHttp);
const api = chai.request(server).keepOpen();
// async/await => doesn't work
describe("GET /user/:id", () => {
it("return user information", async (done) => {
const res = await api
.get("/user/123")
.set("Cookie", "_id=567;locale=en");
chai.expect(res.status).to.equal(200);
done();
});
});
// classic syntax => works
describe("GET /user/:id", () => {
it("return user information", () => {
api
.get("/user/123")
.set("Cookie", "_id=567;locale=en")
.end(function (err, res) {
chai.expect(res).to.have.status(200);
});
});
});
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.
Why does the following test pass?
"use strict";
const
path = require('path'),
Dexter = require('../src/Dexter.js'),
chai = require('chai'),
chaiHttp = require('chai-http'),
expect = chai.expect,
dexterServer = new Dexter(path.resolve(__dirname, 'test/data/sample.har'));
chai.use(chaiHttp);
describe('Rest API', () => {
before(() => {
dexterServer.startUp();
});
it('should\'ve started the server', function () {
chai.request('http://127.0.0.1:1121')
.get('/')
.end(function(err, response){
console.log(response.status);
expect(500).to.equal(400);// This passes? What?
done();
});
});
after(() => {
dexterServer.tearDown();
});
});
When I do a console.log of the response.status, I see a 200. But when I do
expect(response.status).to.equal(400);//response.status is an int
passes the test!
What am I doing wrong?
You forgot to pass done callback. it was treated as sync with 0 assumptions.
it('should\'ve started the server', function (done) {
chai.request('http://127.0.0.1:1121')
.get('/')
.end(function(err, response){
console.log(response.status);
expect(500).to.equal(400);// This passes? What?
done();
});
});
You have to pass done in it, before and after statement to maintain the asynchronous flow.
describe('Rest API', (done) => {
before(() => {
dexterServer.startUp();
});
it('should\'ve started the server', function (done) {
chai.request('http://127.0.0.1:1121')
.get('/')
.end(function(err, response){
console.log(response.status);
expect(500).to.equal(400);// This passes? What?
done();
});
});
after((done) => {
dexterServer.tearDown();
});
});
Mocha supports promises so you could use the fact that chai-http produces promises and just return the promise:
it('should\'ve started the server', function () {
// Return the promise.
return chai.request('http://127.0.0.1:1121')
.get('/')
// Use .then instead of .end.
.then(function(response){
console.log(response.status);
expect(500).to.equal(400);
});
});
If you need to do special processing of errors, you could have a .catch too. Otherwise, you can let Mocha handle any error as a failure.