This is an example of my class:
class MyClass {
constructor() {
this.myLib = new MyLib()
}
myMainMethod = param => {
this.myLib.firstMethod(arg => {
arg.secondMethod(param)
})
}
}
Using Jest, how can I assert that "secondMethod" will be called with I call "myMainMethod".
MyLib is a third-party library.
Here is the unit test solution:
index.ts:
import { MyLib } from './MyLib';
export class MyClass {
myLib;
constructor() {
this.myLib = new MyLib();
}
myMainMethod = (param) => {
this.myLib.firstMethod((arg) => {
this.myLib.secondMethod(arg);
});
};
}
index.test.ts:
import { MyClass } from './';
import { MyLib } from './MyLib';
describe('60840838', () => {
it('should pass', () => {
const firstMethodSpy = jest.spyOn(MyLib.prototype, 'firstMethod').mockImplementationOnce((callback) => {
callback('arg');
});
const secondMethodSpy = jest.spyOn(MyLib.prototype, 'secondMethod').mockReturnValueOnce('fake');
const instance = new MyClass();
instance.myMainMethod('param');
expect(firstMethodSpy).toBeCalledWith(expect.any(Function));
expect(secondMethodSpy).toBeCalledWith('arg');
});
});
unit test results with coverage report:
PASS stackoverflow/60840838/index.test.ts
60840838
✓ should pass (4ms)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 87.5 | 100 | 71.43 | 85.71 |
MyLib.ts | 71.43 | 100 | 33.33 | 66.67 | 3,6
index.ts | 100 | 100 | 100 | 100 |
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.54s, estimated 8s
Related
I'm struggling to figure out how to do this.
example.js
import Logger from "logging-library";
export default function example() {
Logger.error(new Error("Example Error")):
}
example.test.js
test("will log an error", () => {
expect(Logger.error).toHaveBeenCalledWith(new Error("Example Error");
});
The examples I've found might cover mocking an entire library, but don't seem to cover mocking and also asserting how it was called.
unit test solution:
example.js:
import Logger from 'logging-library';
export default function example() {
Logger.error(new Error('Example Error'));
}
example.test.js:
import Logger from 'logging-library';
import example from './example';
jest.mock(
'logging-library',
() => {
return { error: jest.fn() };
},
{ virtual: true },
);
describe('64858662', () => {
afterAll(() => {
jest.resetAllMocks();
});
test('will log an error', () => {
example();
expect(Logger.error).toHaveBeenCalledWith(new Error('Example Error'));
});
});
unit test result:
PASS src/stackoverflow/64858662/example.test.js
64858662
✓ will log an error (5ms)
------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
example.js | 100 | 100 | 100 | 100 | |
------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.373s, estimated 12s
I have a code like this
import fun from '../../../example';
export async function init (props:any){
if (fun()){
doSomething();
}
}
I'm creating unit tests for this code above, but I actually want to mock the implementation of fun in the file only as I can't alter fun in its original file
You can use jest.mock(moduleName, factory, options) to mock ../../../example module.
E.g.
index.ts:
import fun from './example';
export async function init(props: any) {
if (fun()) {
console.log('doSomething');
}
}
example.ts:
export default function fun() {
console.log('real implementation');
return false;
}
index.test.ts:
import { init } from './';
import fun from './example';
import { mocked } from 'ts-jest/utils';
jest.mock('./example', () => jest.fn());
describe('63166775', () => {
it('should pass', async () => {
expect(jest.isMockFunction(fun)).toBeTruthy();
const logSpy = jest.spyOn(console, 'log');
mocked(fun).mockReturnValueOnce(true);
await init({});
expect(logSpy).toBeCalledWith('doSomething');
expect(fun).toBeCalledTimes(1);
logSpy.mockRestore();
});
});
unit test result:
PASS stackoverflow/63166775/index.test.ts (13.298s)
63166775
✓ should pass (33ms)
console.log
doSomething
at CustomConsole.<anonymous> (node_modules/jest-environment-enzyme/node_modules/jest-mock/build/index.js:866:25)
----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files | 100 | 50 | 100 | 100 |
index.ts | 100 | 50 | 100 | 100 | 4
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 15.261s
I am unable to mock 3rd party function call in typescript.
The third party library is moment-timezone and I want to mock the code to get browser timezone to write jest test.
Below is the code I need to mock and return string as 'Australia/Sydney'
moment.tz.guess()
I am trying to use jest.mock() as :-
jest.mock('moment-timezone', () => () => ({ guess: () => 'Australia/Sydney' }));
Here is the solution:
index.ts:
import moment from 'moment-timezone';
export function main() {
return moment.tz.guess();
}
index.spec.ts:
import { main } from './';
import moment from 'moment-timezone';
jest.mock('moment-timezone', () => {
const mTz = {
guess: jest.fn()
};
return {
tz: mTz
};
});
describe('main', () => {
test('should mock guess method', () => {
(moment.tz.guess as jest.MockedFunction<typeof moment.tz.guess>).mockReturnValueOnce('Australia/Sydney');
const actualValue = main();
expect(jest.isMockFunction(moment.tz.guess)).toBeTruthy();
expect(actualValue).toBe('Australia/Sydney');
expect(moment.tz.guess).toBeCalled();
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/58548563/index.spec.ts
main
✓ should mock guess method (6ms)
----------|----------|----------|----------|----------|-------------------|
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.828s, estimated 19s
I'm creating a jQuery object inside a function that I'm testing. I need to mock the prop method call.
How can I do this? I'm using Jest. I tried with Sinon as well, but I couldn't get this working.
Here's my method:
import $ from 'jquery';
export function myFunc(element) {
var htmlControl = $(element);
var tagName = htmlControl.prop('tagName');
}
Here is the unit test solution using sinonjs:
index.ts:
import $ from 'jquery';
export function myFunc(element) {
var htmlControl = $(element);
var tagName = htmlControl.prop('tagName');
}
index.spec.ts:
import proxyquire from 'proxyquire';
import sinon from 'sinon';
import { expect } from 'chai';
describe('myFunc', () => {
it('should mock prop() method', () => {
const el = {};
const propStub = sinon.stub().returnsThis();
const jqueryStub = sinon.stub().callsFake(() => {
return {
prop: propStub
};
});
const { myFunc } = proxyquire('./', {
jquery: jqueryStub
});
myFunc(el);
expect(jqueryStub.calledWith(el)).to.be.true;
expect(propStub.calledWith('tagName')).to.be.true;
});
});
Unit test result with 100% coverage:
myFunc
✓ should mock prop() method (156ms)
1 passing (161ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.spec.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/57461604
I try to figure out how to test this kind of method
// Let's say models === null when we instantiate
public initialize(mongodb: MongoDb): this {
if (!this.models) {
this.models = {
users: new models.UserModel(mongodb),
};
}
return this;
}
public getModels(): Models | null {
return this.models || null;
}
My coverage is still staying that I didn't test the if part... This is not really true because when I ask for the getModels I can test its value (so implicitly the if.
Any idea?
Here is the solution:
index.ts:
import * as models from './models';
import { MongoDb, Models } from './interfaces';
export class UserDataSource {
private models: Models | null = null;
public initialize(mongodb: MongoDb): this {
if (!this.models) {
this.models = {
users: new models.UserModel(mongodb)
};
}
return this;
}
public getModels(): Models | null {
return this.models || null;
}
}
Unit test, index.spec.ts:
import { UserDataSource } from './';
import { MongoDb } from './interfaces';
import * as models from './models';
describe('UserDataSource', () => {
const mockedMongodb: MongoDb = {};
describe('#initialize', () => {
it('should initlialize models correctly', () => {
const userDataSource = new UserDataSource();
const actualValue = userDataSource.initialize(mockedMongodb);
expect(userDataSource.getModels()).toEqual(expect.objectContaining({ users: expect.any(models.UserModel) }));
expect(actualValue).toBe(userDataSource);
});
it('should not initialize models', () => {
const userDataSource = new UserDataSource();
// tslint:disable-next-line: no-string-literal
userDataSource['models'] = [];
const actualValue = userDataSource.initialize(mockedMongodb);
expect(actualValue).toBe(userDataSource);
});
});
describe('#getModels', () => {
it('should get models correctly', () => {
const userDataSource = new UserDataSource();
const actualValue = userDataSource.getModels();
expect(actualValue).toEqual(null);
});
it('should get models correctly and not null', () => {
const userDataSource = new UserDataSource();
// tslint:disable-next-line: no-string-literal
userDataSource['models'] = [];
const actualValue = userDataSource.getModels();
expect(actualValue).toEqual([]);
});
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/52729002/index.spec.ts
UserDataSource
#initialize
✓ should initlialize models correctly (5ms)
✓ should not initialize models (7ms)
#getModels
✓ should get models correctly (1ms)
✓ should get models correctly and not null (1ms)
-----------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
52729002 | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
52729002/models | 100 | 100 | 100 | 100 | |
UserModel.ts | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
-----------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: 6.157s
Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/52729002