Jest mock function's inner variable and change function's behavior? - javascript

i have little experience with jest as i am trying to change variable in my function as we expect to have error thrown if the function's inner variable(baseUrl) changed to null, my function :
buildUrl.js:
export function buildUrl(contentId, data, options = {}) {
let baseUrl = config.has('imgBaseUrl') && config.get('imgBaseUrl');
if (!baseUrl) {
throw new Error('some error');
}
}
i need to mock the baseUrl to value of null for example and test it
buildUrl.test.js
import {buildUrl} from "./buildURL";
....
it('throw an error when "baseUrl" is not configured', () => {
let mockBaseUrl = {baseUrl: null};
jest.mock('./buildURL', ()=> mockBaseUrl);
// jest.mock('../../../config/test', ()=>mockImgBaseUrl); // or mock the config to be used by the function?
expect(()=> buildUrl('1.44444', data[0], defaultOptions)).toThrow(
'some error'
);
});
another approach using jest.fn() didnt work as expected , maybe i am missing something here...

Here is the unit test solution:
baseUrl.js:
import config from './config';
export function buildUrl(contentId, data, options = {}) {
let baseUrl = config.has('imgBaseUrl') && config.get('imgBaseUrl');
if (!baseUrl) {
throw new Error('some error');
}
console.log(baseUrl);
}
config.js:
export default {
config: {},
has(key) {
return !!config[key];
},
get(key) {
return config[key];
}
};
baseUrl.spec.js:
import { buildUrl } from './buildUrl';
import config from './config';
describe('buildUrl', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should throw error when baseUrl is null', () => {
const defaultOptions = {};
const data = ['fake data'];
const hasSpy = jest.spyOn(config, 'has').mockReturnValueOnce(true);
const getSpy = jest.spyOn(config, 'get').mockReturnValueOnce(null);
expect(() => buildUrl('1.44444', data[0], defaultOptions)).toThrowError('some error');
expect(hasSpy).toBeCalledWith('imgBaseUrl');
expect(getSpy).toBeCalledWith('imgBaseUrl');
});
it('should log baseUrl', () => {
const defaultOptions = {};
const data = ['fake data'];
const hasSpy = jest.spyOn(config, 'has').mockReturnValueOnce(true);
const getSpy = jest.spyOn(config, 'get').mockReturnValueOnce('https://github.com/mrdulin');
const logSpy = jest.spyOn(console, 'log');
buildUrl('1.44444', data[0], defaultOptions);
expect(hasSpy).toBeCalledWith('imgBaseUrl');
expect(getSpy).toBeCalledWith('imgBaseUrl');
expect(logSpy).toBeCalledWith('https://github.com/mrdulin');
});
});
Unit test result:
PASS src/stackoverflow/48006588/buildUrl.spec.js (8.595s)
buildUrl
✓ should throw error when baseUrl is null (9ms)
✓ should log baseUrl (7ms)
console.log node_modules/jest-mock/build/index.js:860
https://github.com/mrdulin
-------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-------------|----------|----------|----------|----------|-------------------|
All files | 80 | 83.33 | 33.33 | 77.78 | |
buildUrl.js | 100 | 83.33 | 100 | 100 | 3 |
config.js | 33.33 | 100 | 0 | 33.33 | 4,7 |
-------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 9.768s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/48006588

I think you can do this:
jest.mock('./buildURL', () => {buildUrl: jest.fn()})
buildUrl.mockImplementation(() => {baseUrl: null})
See Jest docs

Related

How to mock Google Cloud Firestore Fieldvalue?

I'm using the #google-cloud/firestore package in my NodeJS project and I'm trying to set up some unit tests using Jest. I'm currently mocking the package like this (using a manual mock in the __mocks__ folder):
const mockFirestore = {
collection: jest.fn(() => mockCollection),
};
module.exports = {
Firestore: jest.fn(() => mockFirestore),
};
This works perfectly for mocking queries, but I'm running into an issue when I try to call a function that uses a Firestore FieldValue like this:
import {Firestore} from '#google-cloud/firestore';
const firestore = new Firestore();
const main = async () => {
await firestore
.collection('foo')
.doc('bar')
.set({
foo: 'baz',
updated: Firestore.FieldValue.serverTimestamp()
}, {merge: true});
}
main();
Any ideas or best-practices on how to mock this?
Thanks!
Just assign mocked static property FieldValue and method to Firestore class. Use mock.mockReturnValueOnce() method to mock the return value of .serverTimestamp() method for different test case.
__mocks__/#google-cloud/firestore/index.js:
const mockFirestore = {
collection: jest.fn().mockReturnThis(),
doc: jest.fn().mockReturnThis(),
set: jest.fn(),
};
const MockFirestore = jest.fn(() => mockFirestore);
MockFirestore.FieldValue = {
serverTimestamp: jest.fn(),
};
module.exports = {
Firestore: MockFirestore,
};
main.js:
import { Firestore } from '#google-cloud/firestore';
const firestore = new Firestore();
const main = async () => {
await firestore.collection('foo').doc('bar').set(
{
foo: 'baz',
updated: Firestore.FieldValue.serverTimestamp(),
},
{ merge: true }
);
};
export { main };
main.test.js:
import { main } from './main';
import { Firestore } from '#google-cloud/firestore';
jest.mock('#google-cloud/firestore');
describe('67268943', () => {
it('should pass', async () => {
Firestore.FieldValue.serverTimestamp.mockReturnValueOnce(2021);
expect(Firestore).toBeCalledTimes(1);
await main();
const firestore = new Firestore();
expect(firestore.collection).toBeCalledWith('foo');
expect(firestore.doc).toBeCalledWith('bar');
expect(firestore.set).toBeCalledWith(
{
foo: 'baz',
updated: 2021,
},
{ merge: true }
);
});
});
unit test result:
PASS examples/67268943/main.test.js (10.868 s)
67268943
✓ should pass (4 ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
main.js | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 12.589 s

Mocking Segment Analytics with Jest in TS

I'm trying to write a unit test that checks if the .track method of the Analytics is called. For some reason the test keeps failing, although calling the function via http does trigger the call. I'm not sure if I've mocked it wrong, or what the issue could be?
index.ts:
import { Request } from "../types"
import { getSecret } from "../src/secrets"
import Analytics from "analytics-node"
const logger = (req: Request) => {
const analytics = new Analytics(<string>process.env.WRITE_KEY);
return analytics.track({
userId: req.userId
});
}
export default logger
index.test.ts:
jest.mock('analytics-node');
import { Request } from "../types"
import logger from "./index"
import Analytics from "analytics-node"
const mockAnalytics = new Analytics(process.env.WRITE_KEY = 'test');
describe('Logger tests', () => {
it(`Should call analytics.track`, () => {
const request: Request = {
userId: 23
}
return logger(request).then(() => {
expect(mockAnalytics.track).toHaveBeenCalled()
});
});
});
You are using Automatic mock by calling jest.mock('analytics-node').
Calling jest.mock('analytics-node') returns a useful "automatic mock" you can use to spy on calls to the class constructor and all of its methods. It replaces the ES6 class with a mock constructor and replaces all of its methods with mock functions that always return undefined. Method calls are saved in theAutomaticMock.mock.instances[index].methodName.mock.calls.
E.g.
index.ts:
import Analytics from 'analytics-node';
export interface Request {
userId: string | number;
}
const logger = (req: Request) => {
const analytics = new Analytics(<string>process.env.WRITE_KEY);
return analytics.track({
userId: req.userId,
anonymousId: 1,
event: '',
});
};
export default logger;
index.test.ts:
import logger, { Request } from './';
import Analytics from 'analytics-node';
jest.mock('analytics-node');
const mockAnalytics = Analytics as jest.MockedClass<typeof Analytics>;
describe('Logger tests', () => {
afterAll(() => {
jest.resetAllMocks();
});
it(`Should call analytics.track`, () => {
const WRITE_KEY = process.env.WRITE_KEY;
process.env.WRITE_KEY = 'test key';
const request: Request = {
userId: 23,
};
logger(request);
expect(mockAnalytics).toBeCalledWith('test key');
expect(mockAnalytics.mock.instances[0].track).toHaveBeenCalled();
process.env.WRITE_KEY = WRITE_KEY;
});
});
unit test result:
PASS examples/65412302/index.test.ts
Logger tests
✓ Should call analytics.track (4 ms)
----------|---------|----------|---------|---------|-------------------
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: 5.954 s
By using "#segment/analytics-next": "1.49.2"
let analyticsInstance: AnalyticsBrowser | null | undefined;
export const loadAnalytics = (props?: LoadAnalyticsProps): typeof analyticsInstance => {
if ((analyticsInstance && analyticsInstance.instance?.initialized) || !props) {
return analyticsInstance;
}
const { segmentAnalyticsKey, anonymousId, userId } = props;
if (!segmentAnalyticsKey) {
return null;
}
analyticsInstance = AnalyticsBrowser.load({ writeKey: segmentAnalyticsKey });
analyticsInstance.setAnonymousId(anonymousId);
analyticsInstance.identify(userId);
return analyticsInstance;
};
mock
const setAnonymousIdMock = jest.fn();
const identifyMock = jest.fn();
jest.mock("#segment/analytics-next", () => {
const originalModule = jest.requireActual("#segment/analytics-next");
return {
__esModule: true,
...originalModule,
AnalyticsBrowser: {
load: () => {
return {
setAnonymousId: setAnonymousIdMock,
identify: identifyMock,
instance: {
initialized: true
}
};
}
}
};
});

write a unit case for event.preventDefault using jest

I am new to jest, i need a help for writing unit case for event.preventDefault using jest in react,
below are the my code,
export const removeValuesFromTextAreaEvent = ({ removeValuesFromTextArea }) => (
event,
rows
) => {
event.preventDefault();
removeValuesFromTextArea({ rowID: rows });
};
I am getting
TypeError: Cannot read property 'preventDefault' of undefined.
The test
describe("removeValuesFromTextAreaEvent", () => {
it("returns a function that calls removeValuesFromTextAreaEvent with rows", () => {
const removeValuesFromTextArea = jest.fn();
const rowID = 1;
const event = { preventDefault: jest.fn() };
removeValuesFromTextAreaEvent({ removeValuesFromTextArea })(event);
expect(event.preventDefault).toHaveBeenCalled();
removeValuesFromTextAreaEvent({ rowID, removeValuesFromTextArea })();
expect(removeValuesFromTextArea).toHaveBeenCalledWith({ rowID });
});
});
Here is the unit test solution:
index.js:
export const removeValuesFromTextAreaEvent = ({ removeValuesFromTextArea }) => (event, rows) => {
event.preventDefault();
removeValuesFromTextArea({ rowID: rows });
};
index.test.js:
import { removeValuesFromTextAreaEvent } from './';
describe('removeValuesFromTextAreaEvent', () => {
it('returns a function that calls removeValuesFromTextAreaEvent with rows', () => {
const removeValuesFromTextArea = jest.fn();
const rowID = 1;
const event = { preventDefault: jest.fn() };
removeValuesFromTextAreaEvent({ removeValuesFromTextArea })(event, rowID);
expect(event.preventDefault).toHaveBeenCalled();
expect(removeValuesFromTextArea).toHaveBeenCalledWith({ rowID });
});
});
unit test results with 100% coverage:
PASS stackoverflow/61014741/index.test.js (8.616s)
removeValuesFromTextAreaEvent
✓ returns a function that calls removeValuesFromTextAreaEvent with rows (3ms)
----------|---------|----------|---------|---------|-------------------
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: 1 passed, 1 total
Snapshots: 0 total
Time: 10.24s

jest.mock - how to check that function inside function has been called

This is a part of my app:
import validationSchema from "./../_validation/report";
const reportModel = require("../models/report");
ctrl.addReport = async (req, res) => {
const { body } = req;
try {
const validatedData = await validationSchema.validate(body);
const report = await reportModel(req.dbConnection).addReport(validatedData);
// something more
} catch (err) {
throw boom.badRequest(err);
}
};
module.exports = ctrl;
I would like to write some unit test but I want to test that addReport from reportModel has been called with the correct argument.
To mock reportModel I use jest.mock like this:
const reportModel = require("../models/report");
jest.mock('../models/report', () => {
return () => {
return {
addReport: jest.fn(() => {
return true
})
}
}
});
// do some tests and call function which call addReport from reportModel
expect(reportModel().addReport).toHaveBeenCalledTimes(1);
I've got 0. How can I check that addReport from reportModel has been called?
You didn't mock ./models/report module correctly. Here is the solution:
ctrl.js:
import validationSchema from './_validation/report';
import reportModel from './models/report';
const ctrl = {};
ctrl.addReport = async (req, res) => {
const { body } = req;
const validatedData = await validationSchema.validate(body);
const report = await reportModel(req.dbConnection).addReport(validatedData);
};
module.exports = ctrl;
./models/report.js:
function reportModel(connection) {
return {
async addReport(data) {
console.log('addReport');
},
};
}
export default reportModel;
./_validation/report.js:
const validationSchema = {
validate(body) {
return body;
},
};
export default validationSchema;
ctrl.test.js:
import reportModel from './models/report';
const ctrl = require('./ctrl');
jest.mock('./models/report', () => {
const mReportModel = {
addReport: jest.fn(() => true),
};
return jest.fn(() => mReportModel);
});
describe('59431651', () => {
it('should pass', async () => {
const mReq = { body: 'mocked data', dbConnection: {} };
const mRes = {};
await ctrl.addReport(mReq, mRes);
expect(reportModel).toBeCalledWith(mReq.dbConnection);
expect(reportModel(mReq.dbConnection).addReport).toHaveBeenCalledTimes(1);
});
});
Unit test result with coverage report:
PASS src/stackoverflow/59431651/ctrl.test.js
59431651
✓ should pass (7ms)
----------------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
59431651 | 100 | 100 | 100 | 100 | |
ctrl.js | 100 | 100 | 100 | 100 | |
59431651/_validation | 100 | 100 | 100 | 100 | |
report.js | 100 | 100 | 100 | 100 | |
----------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.682s, estimated 11s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59431651

jest unit test for AWS lambda

I am new to Node.js. I was trying to write a jest unit test cases for AWS lambda function(for node environment). I used a node module called "lambda-tester" to test it. But the problem with "lambda-tester" is, it will hit the actual service and return the data. I don't want to do that. I need to mock the service call.
So, I wanted to go with the plain old way. But, I have issues with mocking it. Can you help me to write basic unit test case for the below lambda ith mocking the function "serviceFunction" ?
const dataService = require('../dataService');
exports.lambdaService = async event => {
let response = await serviceFunction(event.id);
if (response.code == 200) {
return response;
} else {
return {
statusCode: response.code,
body: JSON.stringify({
message: response.message
})
};
}
};
const serviceFunction = async id => {
return await dataService.retrieveData(id);
};
You can use jest.spyOn(object, methodName, accessType?) method to mock dataService.retrieveData method. And, your serviceFunction function only has one statement, so you can test lambdaService function with it together.
E.g.
index.js:
const dataService = require('./dataService');
exports.lambdaService = async event => {
let response = await serviceFunction(event.id);
if (response.code == 200) {
return response;
} else {
return {
statusCode: response.code,
body: JSON.stringify({
message: response.message
})
};
}
};
const serviceFunction = async id => {
return await dataService.retrieveData(id);
};
dataService.js:
module.exports = {
retrieveData: async id => {
return { code: 200, data: 'real data' };
}
};
index.spec.js:
const { lambdaService } = require('.');
const dataService = require('./dataService');
describe('lambdaService', () => {
beforeEach(() => {
jest.restoreAllMocks();
});
test('should return data', async () => {
const mResponse = { code: 200, data: 'mocked data' };
const mEvent = { id: 1 };
const retrieveDataSpy = jest.spyOn(dataService, 'retrieveData').mockResolvedValueOnce(mResponse);
const actualValue = await lambdaService(mEvent);
expect(actualValue).toEqual(mResponse);
expect(retrieveDataSpy).toBeCalledWith(mEvent.id);
});
test('should return error message', async () => {
const mResponse = { code: 500, message: 'Internal server error' };
const mEvent = { id: 1 };
const retrieveDataSpy = jest.spyOn(dataService, 'retrieveData').mockResolvedValueOnce(mResponse);
const actualValue = await lambdaService(mEvent);
expect(actualValue).toEqual({ statusCode: 500, body: JSON.stringify({ message: mResponse.message }) });
expect(retrieveDataSpy).toBeCalledWith(mEvent.id);
});
test('should throw an error', async () => {
const mEvent = { id: 1 };
const retrieveDataSpy = jest.spyOn(dataService, 'retrieveData').mockRejectedValueOnce(new Error('network error'));
await expect(lambdaService(mEvent)).rejects.toThrowError(new Error('network error'));
expect(retrieveDataSpy).toBeCalledWith(mEvent.id);
});
});
Unit test result with coverage report:
PASS src/stackoverflow/58623194/index.spec.js
lambdaService
✓ should return data (6ms)
✓ should return error message (4ms)
✓ should throw an error (2ms)
----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------------|----------|----------|----------|----------|-------------------|
All files | 90 | 100 | 66.67 | 90 | |
dataService.js | 50 | 100 | 0 | 50 | 3 |
index.js | 100 | 100 | 100 | 100 | |
----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 4.619s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58623194

Categories