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

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

Related

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

How check that method was called with correct param in unit test SinonJs?

How I can check that methods this.athena.startQueryExecution(param) was called with correct param in unit test using sinon? And is it possible?
I have a simple code, which runs query to database:
export class QueryService {
private readonly athena: Athena = new Athena();
constructor() {
}
public async query(date?: Date) {
const param = {
QueryString: this.generateQuery(date),
};
await this.athena.startQueryExecution(param)
.promise();
}
private generateQuery(date?: Date): string {
if (date) {
return `SELECT * FROM TABLE
WHERE date='$date'
} else {
return `SELECT * FROM TABLE
WHERE date='any date'
}
}
My test:
describe('QueryService', () => {
let queryService = new QueryService();
describe('Query', () => {
it('test', async () => {
// given
const date: Date = {date: "2019-01-01"};
// where
queryService.query(date);
// then
});
});
});
Unit test solution:
queryService.ts:
import { Athena } from './athena';
export class QueryService {
private readonly athena: Athena = new Athena();
constructor() {}
public async query(date?: Date) {
const param = {
QueryString: this.generateQuery(date),
};
return this.athena.startQueryExecution(param).promise();
}
private generateQuery(date?: Date): string {
if (date) {
return `SELECT * FROM TABLE WHERE date='$date'`;
} else {
return `SELECT * FROM TABLE WHERE date='any date'`;
}
}
}
athena.ts:
export class Athena {
public startQueryExecution(param) {
return this;
}
public async promise() {
return 'real data';
}
}
queryService.test.ts:
import { QueryService } from './queryService';
import { Athena } from './athena';
import sinon from 'sinon';
describe('60997196', () => {
afterEach(() => {
sinon.restore();
});
it('should pass', async () => {
const startQueryExecutionStub = sinon.stub(Athena.prototype, 'startQueryExecution').returnsThis();
const promiseStub = sinon.stub(Athena.prototype, 'promise').resolves('fake data');
const queryService = new QueryService();
const date: Date = new Date('2019-01-01');
const actual = await queryService.query(date);
sinon.assert.match(actual, 'fake data');
sinon.assert.calledWithExactly(startQueryExecutionStub, {
QueryString: `SELECT * FROM TABLE WHERE date='$date'`,
});
sinon.assert.calledOnce(promiseStub);
});
});
unit test results with coverage report:
60997196
✓ should pass
1 passing (29ms)
-----------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------------|---------|----------|---------|---------|-------------------
All files | 72.73 | 50 | 60 | 72.73 |
athena.ts | 33.33 | 100 | 0 | 33.33 | 3,6
queryService.ts | 87.5 | 50 | 100 | 87.5 | 18
-----------------|---------|----------|---------|---------|-------------------
source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60997196

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

mock a function in nodejs using mocha

I'm writing unit test cases for my node application using mocha on very first time. I have no clear idea about how to mock a function in mocha.
I added the function of my node application. How can a unit test for below mentioned function look like?.
var authHandler = require('./authenticationHandler');
exports.postloginValues = (req, res) => {
var realEdUsername_Update = req.body.uName;
var encodedPassword = Buffer.from(req.body.password).toString('base64');
var jsonData = {
loginName : realEdUsername_Update,
userPassword : encodedPassword
};
var parseData = JSON.stringify(jsonData);
var result = authHandler.validateRealEdUser(parseData, res);
};
index.js:
var authHandler = require('./authenticationHandler');
exports.postloginValues = (req, res) => {
var realEdUsername_Update = req.body.uName;
var encodedPassword = Buffer.from(req.body.password).toString('base64');
var jsonData = {
loginName: realEdUsername_Update,
userPassword: encodedPassword,
};
var parseData = JSON.stringify(jsonData);
var result = authHandler.validateRealEdUser(parseData, res);
};
authenticationHandler.js:
module.exports = {
validateRealEdUser(parseData, res) {},
};
index.test.js:
const { postloginValues } = require('./');
var authHandler = require('./authenticationHandler');
const sinon = require('sinon');
describe('60734436', () => {
it('should pass', () => {
const validateRealEdUserStub = sinon.stub(authHandler, 'validateRealEdUser');
const mReq = { body: { password: '123', uName: 'james' } };
const mRes = {};
postloginValues(mReq, mRes);
sinon.assert.calledWithExactly(
validateRealEdUserStub,
JSON.stringify({ loginName: 'james', userPassword: Buffer.from(mReq.body.password).toString('base64') }),
mRes,
);
validateRealEdUserStub.reset();
});
});
unit test results with coverage report:
60734436
✓ should pass
1 passing (8ms)
--------------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
--------------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 50 | 100 |
authenticationHandler.js | 100 | 100 | 0 | 100 |
index.js | 100 | 100 | 100 | 100 |
--------------------------|---------|----------|---------|---------|-------------------

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

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

Categories