Why is my resetAllMocks not working in jest - node.js

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

Related

Jest has detected open handle for firstValueFrom in nestJS

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

How to use wrapper with beforeEach and afterEach?

Every test I made with jest it's begining with await app.transaction(async(trx) => { and ends with ..rollback..
await app.transaction(async(trx) => {
const a = await update();
expect(a).toBe(something);
await trx.rollback();
});
The actual test is:
const a = await update();
expect(a).toBe(something);
And I want instead of write this wrapper for every test function, just to write within the beforeEach and afterEach.
Since the test is inside of the parameter to transaction you can't really do this in a beforeEach since it will differ based on the test. However you can avoid duplicating the code in each test by writing a helper function like this:
async function wrapper(testFn) {
return app.transaction(async(trx) => {
await testFn();
return trx.rollback();
}
}
// then do this in each test:
it('should work', () => {
await wrapper(async () => {
const a = await update();
expect(a).toBe(something);
});
});

How to mock fs.readdir in jest node.js

In my node.js app, I've written code to read files from a directory using await fs.readdir. When I try to test the code using jest, I can't mock the readdir function. I'm attaching the code.
const util = require('util');
const readdir = util.promisify(fs.readdir);
async function test(){
const files = await readdir("directory path")
return 'success'
}
How to mock readdir using jest
I've tried the below code, it doesn't seem to work
it('test read', async() => {
jest.spyOn(util, 'promisify').mockReturnValueOnce(fs.readdir);
await readdir.mockReturnValueOnce(files);
await expect(await Files.test()).toEqual("success");
})
I'm getting this error, ENOENT: no such file or directory, scandir ''. How to fix this? any ideas?
You can use the fs/promises module, so you don't need to promisify fs functions
// readdir.js
const fs = require('fs/promises');
async function load(directoryPath) {
const files = await fs.readdir(directoryPath);
return 'success';
}
module.exports = { load };
Notice the jest.mock instruction:
// readdir.test.js
const { load } = require('./readdir');
const fs = require('fs/promises');
jest.mock('fs/promises', () => ({
readdir: jest.fn(),
}));
it('calls fs.readdir function with the correct argument', async () => {
fs.readdir.mockResolvedValue();
await load('my-path');
expect(fs.readdir).toHaveBeenCalledWith('my-path');
});
it('returns correct result', async () => {
fs.readdir.mockResolvedValue();
const result = await load('x');
expect(result).toEqual('success');
});
It runs correctly on my local machine, so if it does not run ok there, might be a configuration issue, not really a problem mocking the fs module

Jest: child_process.exec.mockImplentation is not a function

I have a function that uses the child_process.exec function:
//serverUtils.js:
const { promisify } = require('util');
const exec = promisify(require('child_process').exec);
async getUpstreamRepo() {
try {
const forkConfig = (await exec('git remote get-url upstream')).stdout;
let upstreamRepo = forkConfig.replace('git#github.com:', '');
upstreamRepo = upstreamRepo.replace(/\r?\n|\r/g, '');
return upstreamRepo;
} catch (error) {
console.error(error);
throw error;
}
},
After looking at this SO post, I tried to mock the exec call like so:
//serverUtils.test.js:
const child_process = require('child_process');
jest.mock('child_process')
describe('Test Class', () => {
....
it('check upstream repo', async () => {
child_process.exec.mockImplentation(jest.fn().
mockReturnValueOnce('git#github.com:mock/url.git'))
await expect(serverScript.getUpstreamRepo()).
resolves.toEqual('mock/url.git');
});
}
However, I get child_process.exec.mockImplentation is not a function
As the linked post explains, "Jest documentation says that when mocking Node's core modules calling jest.mock('child_process') is required." -- which I clearly do.
The error you are seeing is because you are calling mockImplentation instead of mockImplementation. Unfortunately, when you correct that typo the test still will not pass.
This is because you are calling promisify on exec method, allowing it to be used as a promise. What promisify does under the hood is transform from an asynchronous callback based function (where the callback is placed at last parameter and is called with error as first parameter and data as second) to a promise based function.
So, in order for the promisify method to work, you will have to mock the exec method so that it calls the callback parameter in order for the promise to resolve.
Also, note that you are reading the stdout parameter from the result of the exec call, so in the returned data you will have to send an object with that property.
Having all that into account:
it('check upstream repo', async () => {
child_process.exec.mockImplementation((command, callback) => {
callback(null, { stdout: 'git#github.com:mock/url.git' });
});
await expect(serverScript.getUpstreamRepo()).
resolves.toEqual('mock/url.git');
});
Another posible solution is to directly mock the promisify method:
jest.mock('util', () => ({
promisify: jest.fn(() => {
return jest.fn().mockResolvedValue({ stdout: 'git#github.com:mock/url.git' });
})
}));
describe('Test Class', () => {
it('check upstream repo', async () => {
await expect(serverScript.getUpstreamRepo()).
resolves.toEqual('mock/url.git');
});
});

Accessing .mock property of an automocked function

I have this code:
import * as a from 'a-a';
jest.mock('a-a');
describe('a-a', () => {
beforeAll(async () => {
const x = await a.x(1); // Calls the mock
console.log(x); // 1
console.log(a.x.mock) // Undefined
});
});
The mock function is:
export async function x(data) {
cache.push(data);
console.log('HERE'); // this is printed
return data;
}
The mock of the module is in the __mocks__ directory.
The a.x() calls the mocked function, but a.x.mock is undefined.
How is that possible? Where is the .mock property?
So, after some investigation I found out that the functions declared in the __mocks__ directory aren't wrapped by jest.fn() by default.
Personally I find the thing a bit confusing.
So you can do both
function x(data) {
cache.push(data);
return cache;
}
jest.mock('a-a', () => ({x: x}))
if you do everything in the same file, or
jest.mock('a-a');
and then in the __mocks__/a-a.js file
export const x = jest.fn(async (data) => {
cache.push(data);
return cache;
});

Resources