How to use async/await with chai test? - node.js

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);
});
});
});

Related

sinon.stub isn't working in my api integration tests

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.

Resolution method is overspecified when testing with Mocha and Supertest for Node.js [duplicate]

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())
})

Getting “Error: Resolution method is overspecified”? in beforeEach and afterEach

When I use done in beforeEach or afterEach I get this error “Error: Resolution method is overspecified” and the test fails.
But now if remove the done() all my tests pass but the terminal hangs without exiting the test script.
I am using knex.js as a query builder.
Is there a solution to this problem?
beforeEach(async (done) => {
await db.migrate.rollback(migrationConfig);
await db.migrate.latest(migrationConfig);
await db.seed.run(seedConfig);
done();
});
// cleaning db before running tests
afterEach(async (done) => {
await db.migrate.rollback(migrationConfig);
done();
});
describe("POST /user/login", () => {
it("should return a jwt after loging in user", (done) => {
chai
.request(server)
.post("/user/login")
.send({
email: "saaransh#test.com",
password: "test123",
})
.end((err, res) => {
res.should.have.status(200);
res.should.be.json;
res.body.should.have.property("token");
done();
});
});
});
If you are using async handler functions, then you should not give it parameter done. Promise returned by async function will tell Mocha when executing the function is ready.
beforeEach(async () => {
await db.migrate.rollback(migrationConfig);
await db.migrate.latest(migrationConfig);
await db.seed.run(seedConfig);
});
// cleaning db before running tests
afterEach(async () => {
await db.migrate.rollback(migrationConfig);
});

Using mocha and chai for backend unit tests, how to clear the database before each test

I have a backend setup using Node.js, Express and MongoDB. I wrote two unit tests for one of my endpoints but encountered unexpected behavior. First, here is the code:
let chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../../../server')
const expect = chai.expect
chai.use(chaiHttp);
describe('POST /user/register', () => {
it('should create a new user', (done) => {
chai
.request(server)
.post('/user/register')
.send({username: 'Bob', password: 'password123'})
.end((err, res) => {
expect(res.status).to.equal(201)
done()
})
})
it('should result in an error if there is no payload', (done) => {
chai
.request(server)
.post('/user/register')
.send({})
.end((err, res) => {
expect(res.status).to.equal(500)
done()
})
})
})
On the should create a new user test, it passes on the first try, but fails on the 2nd because the user is persisted in the database, so my endpoint returns a 409 status code, not 201.
I know that I have to use a beforeEach call to clear the test database, I'm just not sure of the implementation after having searched around for a while.
The approach with beforeEach() is correct in your case a before() hook would be even better. Here is the official documentation: https://mochajs.org/#hooks
You would simply change your code to this:
let chai = require('chai');
const chaiHttp = require('chai-http');
const server = require('../../../server')
const expect = chai.expect
chai.use(chaiHttp);
describe('POST /user/register', () => {
before(async () => {
//This is sequelize syntax for postgres. You could just take your mongoose syntax here to delete all users
await User.destroy({
where: {},
truncate: { cascade: true },
})
})
it('should create a new user', (done) => {
chai
.request(server)
.post('/user/register')
.send({username: 'Bob', password: 'password123'})
.end((err, res) => {
expect(res.status).to.equal(201)
done()
})
})
it('should result in an error if there is no payload', (done) => {
chai
.request(server)
.post('/user/register')
.send({})
.end((err, res) => {
expect(res.status).to.equal(500)
done()
})
})
})

500 equals 400 passes in a mocha test?

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.

Resources