Jest mock not resolving to each injected value - javascript

I'm trying to test a Firebase cloud function named myCloudFn in my functions/send.js module. My tests are in functions/test/send.spec.js:
// send.js
const admin = require('firebase-admin');
async function myCloudFn (email) {
const authUser = await admin.auth().getUserByEmail(email);
return authUser;
}
module.exports = { myCloudFn };
// send.spec.js
const send = require('../send.js');
jest.mock('firebase-admin', () => ({
auth: () => ({
getUserByEmail: jest.fn()
.mockResolvedValueOnce({ uid: 'foo-bar' })
.mockResolvedValueOnce(null),
}),
}));
describe('send.js', () => {
it('returns authUser from myCloudFn()', async () => {
const email = 'foo#bar.com';
const responseOptions = [{ uid: 'foo-bar' }, null];
const responsePromises = responseOptions.map(() => send.myCloudFn(email));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toStrictEqual(responseOptions[0]);
expect(responses[1]).toStrictEqual(responseOptions[1]);
});
});
The test passes on the first assertion, but fails on the second. The test returns the same { uid: 'foo-bar' } object both times, but I want the test response value to be null the second time. What am I missing?

A new getUserByEmail spy is created on each auth call, it isn't called more than once.
Instead, it should be:
const mockGetUserByEmail = jest.fn();
jest.mock('firebase-admin', () => ({
auth: () => ({
getUserByEmail: mockGetUserByEmail
})
}));
...
mockGetUserByEmail
.mockResolvedValueOnce({ uid: 'foo-bar' })
.mockResolvedValueOnce(null);
const responsePromises = responseOptions.map(() => send.myCloudFn(email));
...

You can mock the resolved value once of the getUserByEmail method for each test case.
E.g.
send.js:
const admin = require('firebase-admin');
async function myCloudFn(email) {
const authUser = await admin.auth().getUserByEmail(email);
return authUser;
}
module.exports = { myCloudFn };
send.test.js:
const { myCloudFn } = require('./send');
const admin = require('firebase-admin');
jest.mock(
'firebase-admin',
() => {
const mAdmin = {
auth: jest.fn().mockReturnThis(),
getUserByEmail: jest.fn(),
};
return mAdmin;
},
{ virtual: true },
);
describe('send.js', () => {
afterAll(() => {
jest.resetAllMocks();
});
it('should returns authUser from myCloudFn()', async () => {
admin.auth().getUserByEmail.mockResolvedValueOnce({ uid: 'foo-bar' });
const actual = await myCloudFn();
expect(actual).toEqual({ uid: 'foo-bar' });
});
it('should return null', async () => {
admin.auth().getUserByEmail.mockResolvedValueOnce(null);
const actual = await myCloudFn();
expect(actual).toBeNull();
});
});
unit test result:
PASS src/stackoverflow/64575307/send.test.js (14.349s)
send.js
✓ should returns authUser from myCloudFn() (11ms)
✓ should return null (3ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
send.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 17.321s

Related

Jest - replacing mock implementation for named exported function not working

I have the following code of the NodeJS AWS Lambda function I would like to cover by unit tests with Jest:
index.js
const { getConfig } = require('./modules/config/configManager');
const BridgeDataProvider = require('./modules/storage/bridge/bridgeDataProvider');
const ClientProcessor = require('./modules/domain/clientProcessor');
const configPromise = getConfig();
exports.handler = async () => {
const config = await configPromise;
if (!config.enabled) {
return;
}
const bridgeDataProvider = new BridgeDataProvider(config.bridgeDynamoDbConfig);
const clientProcessor = new ClientProcessor(config);
const clients = await bridgeDataProvider.getActiveClients();
for (const client of clients) {
await clientProcessor.process(client);
}
};
And I'm trying to mock getConfig async function for each test individually, but unfortunately it doesn't work. await getConfig() in index.js always returns undefined Here's the testing code:
index.test.js
const { getConfig } = require('../modules/config/configManager');
const { handler } = require('../index');
const BridgeDataProvider = require('../modules/storage/bridge/bridgeDataProvider');
const ClientProcessor = require('../modules/domain/clientProcessor');
jest.mock('../modules/config/configManager', () => ({
getConfig: jest.fn(),
}));
jest.mock('../modules/storage/bridge/bridgeDataProvider');
jest.mock('../modules/domain/clientProcessor');
const defaultMocks = {
BridgeDataProvider: {
getActiveClients: jest.fn().mockImplementation(() => {
/** #type {Client[]} */
const clients = [
{
id: '1234abc',
clientcode: 'Amazon',
},
{
id: '5678def',
clientcode: 'Facebook',
},
];
return Promise.resolve(clients);
}),
},
};
/**
* #typedef {import('../modules/config/config.types').AppConfig} AppConfig
* #typedef {import('../modules/storage/bridge/models.types').Client} Client
*/
describe('handler', () => {
beforeEach(() => {
jest.resetModules()
.clearAllMocks();
setupDefaultMocks();
});
it('does not handle clients if function is not enabled', async () => {
getConfig.mockResolvedValue({enabled: false, bridgeDynamoDbConfig: {}}); // this is not working, await getConfig() returns undefined in index.js
await handler();
const processMethod = ClientProcessor.mock.instances[0].process;
expect(processMethod).toHaveBeenCalledTimes(0);
});
});
function setupDefaultMocks() {
getConfig.mockResolvedValue({enabled: true, bridgeDynamoDbConfig: {}}); // this is not working, await getConfig() returns undefined in index.js
BridgeDataProvider.mockImplementation(() => defaultMocks.BridgeDataProvider);
}
Test output:
FAIL test/index.test.js
● handler › does not handle clients if function is not enabled
TypeError: Cannot read properties of undefined (reading 'enabled')
10 |
11 | const config = await configPromise;
> 12 | if (!config.enabled) {
| ^
13 | return;
14 | }
15 |
at enabled (index.js:12:17)
at Object.<anonymous> (test/index.test.js:48:9)
If I put default implementation right inside jest.mock('../modules/config/configManager', ...) statement, it will mock resolved value as expected. Why is the mocking in individual test not working and how to make it work?
Thanks to #slideshowp2, he pointed me to the fact that getConfig is called at the module scope, and I need to require handler after doing the mock of getConfig. However, if I try to add two tests with differently mocked getConfig (config.enabled = true and config.enabled = false), second test will get the same enabled = true. I put the example below based on the #slideshowp2's answer. I belive I need to remove the index.js module cache after each test. I would be grateful if someone would show how to do it.
index.test.js
const { getConfig } = require('../modules/config/configManager');
const BridgeDataProvider = require('../modules/storage/bridge/bridgeDataProvider');
const ClientProcessor = require('../modules/domain/clientProcessor');
jest.mock('../modules/config/configManager');
jest.mock('../modules/storage/bridge/bridgeDataProvider');
jest.mock('../modules/domain/clientProcessor');
describe('index', () => {
test('should pass (enabled=true)', async () => {
getConfig.mockResolvedValueOnce({ enabled: true, bridgeDynamoDbConfig: 'fake bridge dynamoDB config' });
const bridgeDataProviderInstance = {
getActiveClients: jest.fn().mockResolvedValueOnce([1, 2])
};
BridgeDataProvider.mockImplementation(() => bridgeDataProviderInstance);
const clientProcessorInstance = {
process: jest.fn()
};
ClientProcessor.mockImplementation(() => clientProcessorInstance);
const {handler} = require('../index');
await handler();
expect(getConfig).toBeCalledTimes(1);
expect(BridgeDataProvider).toBeCalledWith('fake bridge dynamoDB config');
expect(bridgeDataProviderInstance.getActiveClients).toBeCalledTimes(1);
expect(clientProcessorInstance.process.mock.calls).toEqual([[1], [2]]);
});
test('should pass (enabled=false)', async () => {
getConfig.mockResolvedValueOnce({ enabled: false, bridgeDynamoDbConfig: 'fake bridge dynamoDB config' });
const bridgeDataProviderInstance = {
getActiveClients: jest.fn().mockResolvedValueOnce([1, 2])
};
BridgeDataProvider.mockImplementation(() => bridgeDataProviderInstance);
const clientProcessorInstance = {
process: jest.fn()
};
ClientProcessor.mockImplementation(() => clientProcessorInstance);
const {handler} = require('../index');
await handler();
expect(clientProcessorInstance.process).toBeCalledTimes(0);
});
});
Test output
FAIL test/index2.test.js
index
√ should pass (enabled=true) (234 ms)
× should pass (enabled=false) (3 ms)
● index › should pass (enabled=false)
expect(jest.fn()).toBeCalledTimes(expected)
Expected number of calls: 0
Received number of calls: 2
43 | const {handler} = require('../index');
44 | await handler();
> 45 | expect(clientProcessorInstance.process).toBeCalledTimes(0);
| ^
46 | });
47 | });
at Object.toBeCalledTimes (test/index2.test.js:45:49)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 2.95 s, estimated 4 s
See es6-class-mocks#replacing-the-mock-using-mockimplementation-or-mockimplementationonce:
Note: you call getConfig in the module scope, so make sure you mock its resolved value before the require('./') statement.
Solution:
index.js:
const { getConfig } = require('./modules/config/configManager');
const BridgeDataProvider = require('./modules/storage/bridge/bridgeDataProvider');
const ClientProcessor = require('./modules/domain/clientProcessor');
const configPromise = getConfig();
exports.handler = async () => {
const config = await configPromise;
if (!config.enabled) {
return;
}
const bridgeDataProvider = new BridgeDataProvider(config.bridgeDynamoDbConfig);
const clientProcessor = new ClientProcessor(config);
const clients = await bridgeDataProvider.getActiveClients();
for (const client of clients) {
await clientProcessor.process(client);
}
};
modules/config/configManager.js:
exports.getConfig = async () => {
return { enabled: false }
}
modules/storage/bridge/bridgeDataProvider.js:
class BridgeDataProvider {
async getActiveClients() {
return []
}
}
module.exports = BridgeDataProvider;
modules/domain/clientProcessor.js:
class ClientProcessor {
async process(client) { }
}
module.exports = ClientProcessor;
index.test.js:
const { getConfig } = require('./modules/config/configManager');
const BridgeDataProvider = require('./modules/storage/bridge/bridgeDataProvider');
const ClientProcessor = require('./modules/domain/clientProcessor');
jest.mock('./modules/config/configManager');
jest.mock('./modules/storage/bridge/bridgeDataProvider');
jest.mock('./modules/domain/clientProcessor');
describe('index', () => {
test('should pass', async () => {
getConfig.mockResolvedValueOnce({ enabled: true, bridgeDynamoDbConfig: 'fake bridge dynamoDB config' })
const bridgeDataProviderInstance = {
getActiveClients: jest.fn().mockResolvedValueOnce([1, 2])
}
BridgeDataProvider.mockImplementation(() => bridgeDataProviderInstance);
const clientProcessorInstance = {
process: jest.fn()
}
ClientProcessor.mockImplementation(() => clientProcessorInstance)
const { handler } = require('./');
await handler();
expect(getConfig).toBeCalledTimes(1);
expect(BridgeDataProvider).toBeCalledWith('fake bridge dynamoDB config');
expect(bridgeDataProviderInstance.getActiveClients).toBeCalledTimes(1);
expect(clientProcessorInstance.process.mock.calls).toEqual([[1], [2]])
})
})
Test result:
PASS stackoverflow/74137912/index.test.js (10.599 s)
index
✓ should pass (87 ms)
---------------------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------------------------|---------|----------|---------|---------|-------------------
All files | 76.19 | 50 | 28.57 | 78.95 |
74137912 | 92.86 | 50 | 100 | 92.31 |
index.js | 92.86 | 50 | 100 | 92.31 | 10
74137912/modules/config | 33.33 | 100 | 0 | 50 |
configManager.js | 33.33 | 100 | 0 | 50 | 2
74137912/modules/domain | 50 | 100 | 0 | 50 |
clientProcessor.js | 50 | 100 | 0 | 50 | 2
74137912/modules/storage/bridge | 50 | 100 | 0 | 50 |
bridgeDataProvider.js | 50 | 100 | 0 | 50 | 3
---------------------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.271 s
Thanks to #slideshowp2, he pointed me to the fact that getConfig is called at the module scope of index.js, and I need to require handler after mocking implementation of getConfig. However, I got the issue of caching the results of getConfig because getConfig is called at the top-level of the module. That makes multiple tests dependent to each other.
Finally I found a way how to do require('../index') in test ignoring the cached state of the module with a help of jest.isolateModules(fn). I added getIsolatedHandler() which returns isolated handler function without any caching state. Here's the final solution:
index.test.js
const { getConfig } = require('../modules/config/configManager');
const BridgeDataProvider = require('../modules/storage/bridge/bridgeDataProvider');
const ClientProcessor = require('../modules/domain/clientProcessor');
jest.mock('../modules/config/configManager');
jest.mock('../modules/storage/bridge/bridgeDataProvider');
jest.mock('../modules/domain/clientProcessor');
describe('index', () => {
test('should pass (enabled=true)', async () => {
getConfig.mockResolvedValueOnce({ enabled: true, bridgeDynamoDbConfig: 'fake bridge dynamoDB config' });
const bridgeDataProviderInstance = {
getActiveClients: jest.fn().mockResolvedValueOnce([1, 2])
};
BridgeDataProvider.mockImplementation(() => bridgeDataProviderInstance);
const clientProcessorInstance = {
process: jest.fn()
};
ClientProcessor.mockImplementation(() => clientProcessorInstance);
const handler = getIsolatedHandler();
await handler();
expect(getConfig).toBeCalledTimes(1);
expect(BridgeDataProvider).toBeCalledWith('fake bridge dynamoDB config');
expect(bridgeDataProviderInstance.getActiveClients).toBeCalledTimes(1);
expect(clientProcessorInstance.process.mock.calls).toEqual([[1], [2]]);
});
test('should pass (enabled=false)', async () => {
getConfig.mockResolvedValueOnce({ enabled: false, bridgeDynamoDbConfig: 'fake bridge dynamoDB config' });
const bridgeDataProviderInstance = {
getActiveClients: jest.fn().mockResolvedValueOnce([1, 2])
};
BridgeDataProvider.mockImplementation(() => bridgeDataProviderInstance);
const clientProcessorInstance = {
process: jest.fn()
};
ClientProcessor.mockImplementation(() => clientProcessorInstance);
const handler = getIsolatedHandler();
await handler();
expect(clientProcessorInstance.process).toBeCalledTimes(0);
});
});
function getIsolatedHandler() {
let handler;
jest.isolateModules(() => {
const index = require('../index');
handler = index.handler;
});
return handler;
}
Test output:
PASS test/index.test.js
index
√ should pass (enabled=true) (263 ms)
√ should pass (enabled=false) (254 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.289 s

how to test cheerio js

this is my code and i don't know how to write test for it:
html.js
const getHtml = async (url) => {
const { data } = await axios.get(url);
return data;
};
const cheerioInit = async (url) => cheerio.load(await getHtml(url));
module.exports = {
cheerioInit,
getHtml
};
i think i should mock this, but don't know how to do that. i wrote this but getting error:
const htmlJs = require("./html");
describe("html", () => {
it("initializes cheerio js", () => {
const mock = jest.spyOn(htmlJs, "cheerioInit");
expect(mock).toHaveBeenCalled();
});
});
this is error:
You can use jest.spyOn(object, methodName) to mock axios.get() method and its resolved value and mock cheerio.load() method with a fake implementation. After executing the getHtml and cheerioInit functions, make assertion for above mocks to check if they have been called with specific arguments.
E.g.
html.js:
const axios = require('axios');
const cheerio = require('cheerio');
const getHtml = async (url) => {
const { data } = await axios.get(url);
return data;
};
const cheerioInit = async (url) => cheerio.load(await getHtml(url));
module.exports = {
cheerioInit,
getHtml,
};
html.test.js:
const { getHtml, cheerioInit } = require('./html');
const axios = require('axios');
const cheerio = require('cheerio');
describe('html', () => {
afterEach(() => {
jest.restoreAllMocks();
});
describe('getHtml', () => {
it('should get html', async () => {
const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: '<div>teresa teng</div>' });
const actual = await getHtml('http://localhost:3000');
expect(actual).toEqual('<div>teresa teng</div>');
expect(getSpy).toBeCalledWith('http://localhost:3000');
});
});
describe('cheerioInit', () => {
it('should initializes cheerio', async () => {
const loadSpy = jest.spyOn(cheerio, 'load').mockImplementation();
const getSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: '<div>teresa teng</div>' });
await cheerioInit('http://localhost:3000');
expect(loadSpy).toHaveBeenCalledWith('<div>teresa teng</div>');
});
});
});
unit test result:
PASS examples/66304918/html.test.js
html
getHtml
✓ should get html (3 ms)
cheerioInit
✓ should initializes cheerio
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
html.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.088 s

TypeError: sns.publish is not a function , jest mock AWS SNS

I am trying to mock AWS.SNS and I am getting error. I referred posts on StackOverflow and could come up with below. Still, I am getting an error. I have omitted the irrelevant portion. Can someone please help me?
Below is my index.ts
import { SNS } from "aws-sdk";
export const thishandler = async (event: thisSNSEvent): Promise<any> => {
// I have omitted other code that works and not related to issue I am facing.
// I am receiving correct value of 'snsMessagetoBeSent' I verified that.
const response = await sendThisToSNS(snsMessagetoBeSent);
} // thishandler ends here
async function sendThisToSNS(thisMessage: snsAWSMessage) {
const sns = new SNS();
const TOPIC_ARN = process.env.THIS_TOPIC_ARN;
var params = {
Message: JSON.stringify(thisMessage), /* required */
TopicArn: TOPIC_ARN
};
return await sns.publish(params).promise();
}
My test case is below
jest.mock('aws-sdk', () => {
const mockedSNS = {
publish: jest.fn().mockReturnThis(),
promise: jest.fn()
};
return {
SNS: jest.fn(() => mockedSNS),
};
});
import aws, { SNS } from 'aws-sdk';
const snsPublishPromise = new aws.SNS().publish().promise;
import { thishandler } from "../src/index";
describe("async testing", () => {
beforeEach(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
it("async test", async () => {
const ENRICHER_SNS_TOPIC_ARN = process.env.ENRICHER_SNS_TOPIC_ARN;
process.env.ENRICHER_SNS_TOPIC_ARN = "OUR-SNS-TOPIC";
const mockedResponseData ={
"Success": "OK"
};
(snsPublishPromise as any).mockResolvedValueOnce(mockedResponseData);
const result = await thishandler(thisSNSEvent);
});
I get error as TypeError: sns.publish is not a function
Here is the unit test solution:
index.ts:
import { SNS } from 'aws-sdk';
export const thishandler = async (event): Promise<any> => {
const snsMessagetoBeSent = {};
const response = await sendThisToSNS(snsMessagetoBeSent);
return response;
};
async function sendThisToSNS(thisMessage) {
const sns = new SNS();
const TOPIC_ARN = process.env.THIS_TOPIC_ARN;
const params = {
Message: JSON.stringify(thisMessage),
TopicArn: TOPIC_ARN,
};
return await sns.publish(params).promise();
}
index.test.ts:
import { thishandler } from './';
import { SNS } from 'aws-sdk';
jest.mock('aws-sdk', () => {
const mSNS = {
publish: jest.fn().mockReturnThis(),
promise: jest.fn(),
};
return { SNS: jest.fn(() => mSNS) };
});
describe('59810802', () => {
let sns;
beforeEach(() => {
sns = new SNS();
});
afterEach(() => {
jest.clearAllMocks();
});
it('should pass', async () => {
const THIS_TOPIC_ARN = process.env.THIS_TOPIC_ARN;
process.env.THIS_TOPIC_ARN = 'OUR-SNS-TOPIC';
const mockedResponseData = {
Success: 'OK',
};
sns.publish().promise.mockResolvedValueOnce(mockedResponseData);
const mEvent = {};
const actual = await thishandler(mEvent);
expect(actual).toEqual(mockedResponseData);
expect(sns.publish).toBeCalledWith({ Message: JSON.stringify({}), TopicArn: 'OUR-SNS-TOPIC' });
expect(sns.publish().promise).toBeCalledTimes(1);
process.env.THIS_TOPIC_ARN = THIS_TOPIC_ARN;
});
});
Unit test results with 100% coverage:
PASS src/stackoverflow/59810802/index.test.ts (13.435s)
59810802
✓ should pass (9ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.446s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59810802

How to mock a service in Jest with JavaScript?

My code looks like this
const { MyClient } = require('some-service')
const invokeMe = async (input1, input2) => {
const client = new MyClient({
name: 'my-name'
})
return await client.invoke({
input1,
input2
}).catch((err) => {
throw err
})
}
This is what I have, how do I properly mock this service and spy on what it's called with?
const { MyClient } = require('some-service')
describe('test my client', () => {
it('invoke my client', () => {
const response = {
data: []
}
expect(invokeMe('abc', '123')).resolves.toEqual(response)
expect(MyClient).toBeCalledWith({
input1: 'abc',
input2: '123'
})
})
})
Edit: Why does the below still call the original function?
it('invoke my client', () => {
const mockInvoke = jest.fn().mockImplementation(() => Promise.resolve({
data: []
}))
const mockMyClient = () => {
return { invoke: mockInvoke }
}
const mockSomeService = {
MyClient: mockMyClient
}
jest.doMock('some-service', () => mockSomeService
...
})
You can use jest.mock(moduleName, factory, options) mock the imported service
E.g.
index.js:
const { MyClient } = require('./some-service');
const invokeMe = async (input1, input2) => {
const client = new MyClient({
name: 'my-name',
});
return await client
.invoke({
input1,
input2,
})
.catch((err) => {
throw err;
});
};
module.exports = invokeMe;
some-service.js:
class MyClient {
async invoke(input1, input2) {
return 'real response';
}
}
module.exports = { MyClient };
index.test.js:
const invokeMe = require('./');
const { MyClient } = require('./some-service');
jest.mock('./some-service', () => {
const mMyClient = { invoke: jest.fn() };
return { MyClient: jest.fn(() => mMyClient) };
});
describe('60008679', () => {
it('should invoke', async () => {
const client = new MyClient();
client.invoke.mockResolvedValueOnce('fake response');
const actual = await invokeMe('a', 'b');
expect(actual).toBe('fake response');
expect(MyClient).toBeCalledWith({ name: 'my-name' });
expect(client.invoke).toBeCalledWith({ input1: 'a', input2: 'b' });
});
it('should handle error', async () => {
const client = new MyClient();
const mError = new Error('some error');
client.invoke.mockRejectedValueOnce(mError);
await expect(invokeMe('a', 'b')).rejects.toThrowError(mError);
expect(MyClient).toBeCalledWith({ name: 'my-name' });
expect(client.invoke).toBeCalledWith({ input1: 'a', input2: 'b' });
});
});
Unit test results with 100% coverage:
PASS src/stackoverflow/60008679/index.test.js (11.029s)
60008679
✓ should invoke (8ms)
✓ should handle error (4ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 12.314s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/60008679

JestJS: How to test for calling function in a nested (async) function

I'm trying to write a unit test for a nested function, which looks like this:
myFunction.js
const anotherFunction = require('./anotherFunction.js')
module.exports = (app, io) => {
return (req, res) => {
const { id, value } = req.query
req.app.locals['target' + id].pwmWrite(value)
anotherFunction(app, io)
res.send({ value })
}
}
I would like to test if pwmWrite() and anotherFunction() have been called.
But I've got some problems because of the return (req, res) => {} and because of the imported function.
This is my attempt, which is not working:
myFunction.test.js
test('should call pwmWrite() and anotherFunction()', async () => {
const app = {}
const io = { emit: jest.fn() }
const req = {
app: {
locals: {
target1: { pwmWrite: () => 25 }
}
}
}
}
expect.assertions(1)
expect(req.app.locals.target1.pwmWrite).toHaveBeenCalled()
await expect(myFunction(app, io)).resolves.toEqual(25)
})
Here is the solution:
myFunction.js:
const anotherFunction = require('./anotherFunction.js');
module.exports = (app, io) => {
return (req, res) => {
const { id, value } = req.query;
req.app.locals['target' + id].pwmWrite(value);
anotherFunction(app, io);
res.send({ value });
};
};
anotherFunction.js:
module.exports = (app, io) => {
return 'do something';
};
Unit test:
jest.mock('./anotherFunction');
const myFunction = require('./myFunction');
const anotherFunction = require('./anotherFunction');
describe('test suites', () => {
test('should call pwmWrite() and anotherFunction()', () => {
const app = {};
const io = { emit: jest.fn() };
const id = '1';
const value = 'jest';
const req = {
query: { id, value },
app: {
locals: {
target1: { pwmWrite: jest.fn() }
}
}
};
const res = { send: jest.fn() };
myFunction(app, io)(req, res);
expect(anotherFunction).toBeCalledWith(app, io);
expect(req.app.locals.target1.pwmWrite).toBeCalledWith(value);
});
});
Unit test result with coverage report:
PASS src/stackoverflow/52845000/myFunction.spec.js
test suites
✓ should call pwmWrite() and anotherFunction() (5ms)
--------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
--------------------|----------|----------|----------|----------|-------------------|
All files | 88.89 | 100 | 66.67 | 88.89 | |
anotherFunction.js | 50 | 100 | 0 | 50 | 2 |
myFunction.js | 100 | 100 | 100 | 100 | |
--------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.113s
Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/52845000

Categories

Resources