Jest has detected open handle for firstValueFrom in nestJS - jestjs

I get this error with firstValueFrom when using NestJS + Axios HttpService.
Jest has detected the following 1 open handle potentially keeping Jest from exiting:
await firstValueFrom(this.httpService.get(url, { headers }));
But at the same time, jest completes successfully and my requests do not hang. What is the right thing to do in this situation?
Don't suggest --forceExit :)
UPD #1:
public async getData() {
const url = 'correct url here :) ';
const headers = {};
return await firstValueFrom(this.httpService.get(url, { headers }));
}
Test:
describe('Service', () => {
describe('Main', () => {
it('get data', async () => {
const result = await service.getData();
expect(result.data).toBeDefined();
});
});
});

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

Test cases where Axios errors need to be handled differently, using Jest

Assuming that I have the following function, in which I want to handle different axios erroneous responses in different ways.
async fetchData() {
const data = {...};
const response = await axios.post('endpoint_here', data)
.catch((error) => {
if (error.code === SomeKindOfError) {
throw new SomeKindOfError(error.message);
}
if (error.code === AnotherKindOfError) {
throw new AnotherKindOfError(error.message);
}
throw new Error(error.message);
});
return response.data;
}
How can I test all these different cases in my test using Jest ?
At the moment my test is this:
test('test SomeKindOfError', async () => {
axios.post.mockRejectedValueOnce(new SomeKindOfError('There was an error.'));
await expect(fetchData())
.rejects
.toThrowError(new SomeKindOfError('There was an error.'));
});
The above test is a pass, but it is nothing close to what a proper test for this function should be.
I would want to be able to check what the error returned from Axios is, and accordingly test that my own custom made errors get thrown.
Ideally, a way of mocking the Axios error response that will allow me to cover all the different cases in my tests, something like the following:
test('test SomeKindOfError', async () => {
axios.post.mockImplementation(() => Promise.reject({
error: { code: SomeKindOfError.code },
}));
await expect(fetchData())
.rejects
.toThrowError(new SomeKindOfError('There was an error.'));
});
Eventually, the solution was as simple as this:
test('test SomeKindOfError', async () => {
axios.post.mockRejectedValueOnce({ code: SomeKindOfError });
await expect(fetchData())
.rejects
.toThrowError(new SomeKindOfError('There was an error.'));
});

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.

How to close Express server inside Jest afterAll hook

I am trying to write integration tests for my Express server using Jest. Since Jest runs tests in parallel (and I would like to avoid running tests in sequence using --runInBand), I am using the get-port library to find a random available port so that different test suites don't have port collisions.
My tests all run successfully, the only problem is that the server is failing to close down properly inside the afterAll hook. This causes Jest to print the following in the console...
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests.
Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
When I use the --detectOpenHandles flag, Jest just hangs after tests complete. Nothing gets printed to the console.
Here is my test code...
let axios = require('axios')
const getPort = require('get-port')
const { app } = require('../../index')
const { Todo } = require('../../models')
// set reference to server to access
// from within afterAll hook
let server
beforeAll(async () => {
const port = await getPort()
axios = axios.create({ baseURL: `http://localhost:${port}` })
server = app.listen(port)
})
afterAll(() => {
server.close()
})
describe('GET /todos', () => {
it('returns a list of todos', async () => {
const { data: todos } = await axios.get('/todos')
todos.forEach(todo => {
expect(Todo.validate(todo)).toEqual(true)
})
})
})
I am on that github thread on this issue. Here is exactly the configuration that works for me. In package.json
"test": "jest --no-cache --detectOpenHandles --runInBand --forceExit",
Here is the configuration in test file
afterEach(async () => {
await server.close();
});
afterAll(async () => {
await new Promise(resolve => setTimeout(() => resolve(), 10000)); // avoid jest open handle error
});
beforeEach(() => {
// eslint-disable-next-line global-require
server = require('../index');
jest.setTimeout(30000);
});
OR you have only afterAll to set timeout and settimeout for each test in the test body individually.That's example below
afterEach(async () => {
await server.close();
});
afterAll(async () => {
await new Promise(resolve => setTimeout(() => resolve(), 10000)); // avoid jest open handle error
});
beforeEach(() => {
// eslint-disable-next-line global-require
server = require('../index');
});
describe('POST /customers', () => {
jest.setTimeout(30000);
test('It creates a customer', async () => {
const r = Math.random()
.toString(36)
.substring(7);
const response = await request(server)
.post('/customers')
.query({
name: r,
email: `${r}#${r}.com`,
password: 'beautiful',
});
// console.log(response.body);
expect(response.body).toHaveProperty('customer');
expect(response.body).toHaveProperty('accessToken');
expect(response.statusCode).toBe(200);
});
});
The root cause is that the express app server is still running after the tests complete. So the solution is to close the server.
In the main server file:
export const server = app.listen(...)
In the test file:
import { server } from './main-server-file'
afterAll(() => {
server.close();
});
Using nodejs 17.4.0, jest 27.5.1, supertest 6.2.2. Running test with
cross-env NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest

How do I test this async method call in reactjs using mocha

// Balance.jsx
...
updateToken () {
const parseResponse = (response) => {
if (response.ok) {
return response.json()
} else {
throw new Error('Could not retrieve access token.')
}
}
const update = (data) => {
if (data.token) {
this.data.accessTokenData = data
} else {
throw new Error('Invalid response from token api')
}
}
if (this.props.balanceEndpoint !== null) {
return fetch(this.props.accessTokenEndpoint, {
method: 'get',
credentials: 'include'
})
.then(parseResponse)
.then(update)
.catch((err) => Promise.reject(err))
}
}
componentDidMount () {
this.updateToken()
.then(() => this.updateBalance())
}
}
// Test
it('updates the balance', () => {
subject = mount(<Balance {...props} />)
expect(fetchMock.called('balance.json')).to.be.true
})
I can't figure out how to test the above using Mocha. The code is does work the method updateBalance is called and the fetch api call actually does happen, but the test still fails. If I call updateBalance() synchronously it passes... How do I tell the test to wait for the promise to resolve?
You don't really say what you want to test that the
method does, but if all you want to test is that the method resolves on a network call, then there is no need for Sinon or any of that, as this is all you need:
describe("BalanceComponent", () => {
it("should resolve the promise on a successful network call", () => {
const component = new BalanceComponent({any: 'props', foo: 'bar'});
// assumes you call a network service that returns a
// successful response of course ...
return component.updateToken();
});
});
This will test that the method actually works, but it is slow and is not a true unit test, as it relies on the network being there and that you run the tests in a browser that can supply you with a working implementation of fetch. It will fail as soon as you run it in Node or if the service is down.
If you want to test that the method actually does something specific, then you would need to to that in a function passed to then in your test:
it("should change the token on a successful network call", () => {
const component = new BalanceComponent({any: 'props', foo: 'bar'});
const oldToken = component.data.accessTokenData;
return component.updateToken().then( ()=> {
assert(oldToken !== component.data.accessTokenData);
});
});
If you want to learn how to test code like this without being reliant on there being a functioning link to the networked service you are calling, you can check out the three different techniques described in this answer.

Resources