chai-as-promised's eventually passes wrong rejection - node.js

suddenly I realized that in many of my tests it wrongly passes the fail test, I tried to understand the reason, here's is an example that passes the test which is wrong
describe('...', () => {
it('...', () => {
return chai.expect(Promise.reject('boom')).to.eventually.not.rejected;
});
});
can anyone help me to figure out what am doing wrong? thanks

to.not.eventually is not the same as to.eventually.not
You’re using not, it should be before the eventually
So change your test to below code to use to.not.eventually to see it won’t get passed and it’ll fail
const { describe, it } = require('mocha');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const { expect } = chai;
describe('...', () => {
it('...', () => {
return expect(Promise.reject(new Error('boom'))).to.not.eventually.rejected;
});
});

Related

Why is my resetAllMocks not working in jest

The second expect(fs.writeFile).toHaveBeenCalledTimes(1) (in describe('Guid for MPX') returns an error because the writeFile has been called twice. In theory, jest.ResetAllMocks should take care of this but it doesn’t.
'use strict';
const fs = require('fs').promises;
const path = require('path');
const guidForMpxInvalid = require('../json/guid-for-Mpx-invalid.json')
const data = require('../../../data/sandbox-data-model.json');
jest.mock('fs', () => ({
promises: {
writeFile: jest.fn(),
},
}));
const {
writeData,
createGuidForMpx,
createMpxForGuid,
} = require('../app/transform');
const directoryPath = path.join(__dirname, '../../../wiremock/stubs/mappings');
describe('Write file', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('should write a file', async () => {
const result = await writeData(guidForMpxInvalid, 'guid-for-Mpx-invalid-Mpx.json');
expect(result).toEqual('guid-for-Mpx-invalid-Mpx.json written');
expect(fs.writeFile).toHaveBeenCalledTimes(1);
});
});
describe('Guid for MPX', () => {
it('should create JSON file for the GUID of a particular MPX', async ()=>{
const result = await createGuidForMpx(data.Customers[0].guid, data.Customers[0].Customer_Overlays.core.Personal_Details.MPX);
expect(result).toEqual('guid-for-Mpx-AB123456B.json written');
expect(fs.writeFile).toHaveBeenCalledTimes(1);
});
});
The code being called:
const writeData = async (data, file) => {
const directoryPath = path.join(__dirname, '../../wiremock/stubs/mappings');
try {
fs.writeFile(`${directoryPath}/${file}`, data);
return `${file} written`
} catch (err) {
return err;
}
};
I was experiencing the same problem until I placed jest.resetAllMocks(); inside afterEach like so:
afterEach(() => {
jest.resetAllMocks();
});
I eventually got this working by creating a spy for the writefile at the start of each test and clearing it when the test is done:
it('should write a file', async () => {
const writeFileSpy = jest.spyOn(fs, 'writeFile');
const result = await writeData(guidForMPXInvalid, 'guid-for-mpx-invalid-mpx.json');
expect(result).toEqual('guid-for-mpx-invalid-mpx.json written');
expect(writeFileSpy).toHaveBeenCalledTimes(1);
writeFileSpy.mockClear();
});
});
Same thing here. I had to use spyOn as well, which is probably better practice.
All should beware when not using spyOn with complex libraries, double check that your reset works, but safe practice is to manually restore the function you mocked.
There is an issue it seems, perhaps because how fs/promises is included. fs.promises is a Getter function and is lazy loaded from internal/fs/promises, and jest is seemingly unable to clean lazy loaded modules with jest.resetModules?
See related note by #john-james regarding moduleNameMapper:
Jest not working with fs/promises typescript
Another documented error with resetModules():
https://github.com/facebook/jest/issues/11632

Jest, data in one test bleeding to another

I have been facing this issue with jest, where the data i set in one test is creeping into another test and causing it to fail.
Let me try to show some code
spec.ts
const MOCK_PRODUCT = require('../dummydata/dummydata.json');
describe('function 1', () => {
test('something', () => {
const productData = MOCK_PRODUCT;
expect(...) // works fine
});
test('something else', () => {
const productData = MOCK_PRODUCT;
productData.someField.push({...})
expect(...) // works fine
});
});
describe('function 2', () => {
test('something more', () => {
const productData = MOCK_PRODUCT;
expect(...) // fails
});
});
My test file goes like this. There is a describe block for each function and multiple tests inside the describe block.
What i observe is that, when i changed the productData in second test as you see above, the changed data is available in all the tests in the next describe block, causing them to fail.
Am i doing something wrong? is something missing?
It seems MOCK_PRODUCT is a object, then when you set const productData = MOCK_PRODUCT;, the productData is a referent to MOCK_PRODUCT. This means if you update producData content, MOCK_PRODUCT content also be updated.
In the previous test, you update productData object, then the next test will not working as your expected.
A simple way to avoid it is to stop using referent way, just clone to a new object for every test.
const productData = {...MOCK_PRODUCT};
My recommendation, define productData variable at the top level, assign cloned MOCK_PRODUCT for it in beforeEach. Then use productData in each test.
Unfortunately that is by design. The script is just run from top to bottom. beforeAll, beforeEach, afterEach and afterAll functions could help you to factor out some code from your test methods.
You can also use the cloneDeep function from lodash to make an completely independent copy of the test data before each test runs.
const MOCK_PRODUCT = require('../dummydata/dummydata.json');
const _ = require('lodash'),
let productData = undefined;
beforeEach(() => {
productData = _.cloneDeep(MOCK_PRODUCT);
})
describe('function 1', () => {
test('something', () => {
expect(...) // works fine
});
test('something else', () => {
productData.someField.push({...})
expect(...) // works fine
});
});
describe('function 2', () => {
test('something more', () => {
expect(...) // fails
});
});

Mocha times out calling async promise chain in Before hook despite using done

I'm running suite of async integration tests into a mongoose db using mocha and chai on node.js. Most are running fine but for one I have to carry out some pre-test db-prep with the before hook. When I use Done in the before hook Mocha times out.
Error I'm getting is "Error: Timeout of 5000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/donfelipe/Sites/Agents/test/agents.js)"
Have tried moving the done() into a finally block at the end of the promise chain, but that just results in the the it block running before the async chain has finished executing. Kind of stumped.
/*
test case to check that the get function returns
*/
process.env.NODE_ENV = 'test-db';
'use strict'
const mongoose = require("mongoose");
const schemas = require('../app_api/models/schemas');
const agents = require('../app_api/controllers/agents.js');
const lists = require('../app_api/controllers/lists.js');
const server = require('../app.js');
const assert = require('assert')
const config = require('config')
//Require the dev-dependencies
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
chai.use(chaiHttp);
describe('Agents test Suite', () => {
beforeEach(() => {
//don't have anything too do in this case
})
describe('/GET listAddItem suite', function () {
//before we can test deletion we have to add at least one list in.
const userModel = mongoose.model('User', schemas.userSchema);
const agentModel = mongoose.model('Agent', schemas.agentSchema);
let agentToAddId = {}
const listObject = {
listName: 'testList',
_id: mongoose.Types.ObjectId(),
agents: [{
_id: "00000000000000000000000",
name: "Test agent"
}]
}
const lengthToTestAgainst = listObject.agents.length
beforeEach((done) => {
userModel.findById(config.defaultUserId)
.select('agentList')
.then((parentDoc) => {
if (!parentDoc) {
console.error('Unable to find user');
}
parentDoc.agentList.push(listObject)
return parentDoc.save()
})
.then(() => {
return agentModel.find({})
})
.then((response) => {
agentToAddId = response[0]._id
//console.log(response[0]._id);
done()
})
})
it('should add a new item into the testList', (done) => {
chai.request(server)
.get(`/agents_api/listAddItem/${config.defaultUserId}/${listObject._id}/${agentToAddId}`)
.end((err, response) => {
response.should.have.status(200)
response.body.agentList.testList.should.not.be.equal(lengthToTestAgainst + 1)
done(err)
})
})
})
})
Duh. Resolved this myself. A real case of the tail wagging the dog.
So I mocked up the call to the API that the chai.request(server) is making:
/agents_api/listAddItem/${config.defaultUserId}/${listObject._id}/${agentToAddId}
and submitted it through POSTMAN. Turns out there was a bug in the API and so it was not returning a response, so the timeout I was getting from Mocha was a valid response, the request was just hanging.

Clearing collection before testing with Mocha

I am writing unit tests for my Node.js app using Mocha, Chai, and Mongoose. The tests themselves work if the collection is empty(as desired), but I'm having trouble clearing the collection before testing.
let mongoose = require("mongoose");
let Subject = require('../Subject/subject');
//Require the dev-dependencies
let chai = require('chai');
let chaiHttp = require('chai-http');
// let server = require('../server');
let server = "http://localhost:3000"
let should = chai.should();
chai.use(chaiHttp);
describe('subjects', () => {
before((done) => { //Before each test, empty the database
Subject.remove({})
done();
});
describe('/GET subject', () => {
// working test
});
describe('/POST subject', () => {
// working test
});
describe('/GET subject', () => {
// working test
});
});
I have also tried variations of
Subject.deleteMany({}, (err) => console.log(err));
and
Subject.find({}, (subject)=>{
subject.remove({}).exec()
})
inside the before block to no avail. I have tried the removes outside of the before block as well, just in case. If I console.log(Subject.remove({})) I get the Subject object, so it's able to access it, just not actually doing anything permanent with it.
I've been at this a couple hours now and haven't gotten any closer to figuring it out, so all help is appreciated.
Try waiting for the callback of the remove call like so
before((done) => { //Before each test, empty the database
Subject.remove({}, done);
});
Since both Mocha and Mognoose support promises for asynchronous blocks, they can be seamlessly used instead of calling done directly. In this case a promise should be returned:
before(() => Subject.remove({}));

Issues creating a Unit Test stub for this function return using Sinon and Should

I have the following health.js file..
exports.endpoint = function (req, res) {
let http_status = 200;
res.sendStatus(http_status);
};
Where I'm trying to figure out how I can create a Unit Test from this endpoint.
Below is my attempt so far..
import { sinon, should } from "../../test-harness";
let health = require('../../../src/endpoints/health');
health.endpoint = sinon.stub();
describe('The health endpoint', function () {
it('should call .endpoint()', () => {
health.endpoint.should.have.been.called;
});
});
But it seems to be passing regardless of if I add a .not to my should.have.been.called statement.
Where am I going wrong?

Resources