mock a specific function in a module - javascript

I have two functions in one file
file1.ts:
const function1 = () => {
return 123;
};
const function2 = () => {
return function1() + 2;
};
export { function1, function2 };
I'm writing unit tests using jest for function 2. but I'm unable to mock function1
I just tried to use jest.spyOn to mock function1
import * as helperFunctions from 'file1';
describe('test function2 ', () => {
let functionSpy: any;
beforeAll(() => {
functionSpy = jest.spyOn(helperFunctions, 'function1 ');
});
afterAll(() => {
functionSpy.mockReset();
});
test('test', () => {
functionSpy.mockReturnValue(1);
expect(helperFunctions.function2()).toEqual(3);
});
});
in my test, function1 is not mocked, it still calls the actual implementation.
Any help is appreciated.

Here is the solution:
index.ts:
const function1 = () => {
return 123;
};
const function2 = () => {
return exports.function1() + 2;
};
exports.function1 = function1;
exports.function2 = function2;
Unit test:
describe('test function2 ', () => {
const helperFunctions = require('./');
helperFunctions.function1 = jest.fn();
test('test', () => {
helperFunctions.function1.mockReturnValueOnce(1);
expect(helperFunctions.function2()).toEqual(3);
});
});
Unit test result:
PASS src/stackoverflow/56354252/index.spec.ts
test function2
✓ test (8ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.696s, estimated 3s
You can find this issue for more detail: https://github.com/facebook/jest/issues/936#issuecomment-214939935

Related

Jest mocking a module with module pattern and having unique functionality on each test

I'm having trouble having a unique implementation of search from this module pattern setup I have (MyConnection.js) - here's similar to what I have:
// MyConnection.js
const MyConnection = () => {
const search = async (q) => {
//...some functionality
};
return {
search,
};
};
//JEST TESTS
const MyConnection = require('../MyConnection')
// this works - but it sets the search implementation
// for the whole test file I believe
jest.mock('../MyConnection', () => {
return jest.fn(()=>{
search: ()=> ['mocked', 'fn', 'results']
})
})
//the jest tests - I want different
// implementations of search in each test
describe('connection tests', ()=>{
it('test one', ()=>{
//Not sure if its something like this to set 'search' for each test? This doesn't work as is
MyConnection.search.mockImplementation((q)=>{`You searched ${q}`})
})
it('test two', ()=>{
MyConnection.search.mockImplementation((q)={q.length>32? 'a': 'b' }})
})
})
How can I get unique Jest mock implementations of that search function for each test?
You should make sure that mock MyConnection always returns the same connection object in your test file and the module you want to test.
MyConnection.js:
const MyConnection = () => {
const search = async (q) => {};
return {
search,
};
};
module.exports = MyConnection;
main.js:
const MyConnection = require('./MyConnection');
async function main(q) {
const conn = MyConnection();
return conn.search(q);
}
module.exports = main;
main.test.js:
const MyConnection = require('./MyConnection');
const main = require('./main');
jest.mock('./MyConnection', () => {
console.log('MyConnection module gets mocked');
const conn = { search: jest.fn() };
return jest.fn(() => conn);
});
const mConn = MyConnection();
describe('connection tests', () => {
it('test one', async () => {
mConn.search.mockImplementation((q) => {
return `You searched ${q}`;
});
const actual = await main('teresa teng');
expect(actual).toBe('You searched teresa teng');
});
it('test two', async () => {
mConn.search.mockImplementation((q) => {
return q.length > 32 ? 'a' : 'b';
});
const actual = await main('_');
expect(actual).toBe('b');
});
});
test result:
PASS examples/70132655/main.test.js (10.454 s)
connection tests
✓ test one (2 ms)
✓ test two (1 ms)
console.log
MyConnection module gets mocked
at examples/70132655/main.test.js:5:11
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.903 s
You can't mock it like this:
jest.mock('./MyConnection', () => {
console.log('MyConnection module gets mocked');
return jest.fn(() => { search: jest.fn() });
});
Why? Because every time you call the MyConnection() function in the test file or the file you want to test. It will return a new mock object({ search: jest.fn() }), the mock objects in file under test and test case are not same.

Restore implementation after mocking jest

I'm trying to mock a function and after testing use the real implementation for other tests.
const myService = () => {
return {
foo: () => return 1;
}
}
const myService = require('./myService.js');
jest.mock('./myService');
describe('testing myService', () => {
it('should return 2 by mocked', () => {
myService.mockImplementation(() => ({
foo: jest.fn().mockResolvedValueOnce(2),
}));
expect.assertions(1);
expect(myService().foo()).toBe(2);
jest.restoreAllMocks();
});
it('should return 1', () => {
expect(myService().foo()).toBe(1);
});
});
But after this, foo() is still mocked.
jest.spyOn(object, methodName) is a better choice to create a mock method for an object.
We can restore the method to original implementation by using jest.restoreAllMocks() inside afterEach() hook.
Beware that jest.restoreAllMocks() only works when the mock was created with jest.spyOn; other mocks will require you to manually restore them.
E.g.
myService.js:
const myService = () => {
return {
foo: () => 1,
};
};
module.exports = myService;
myService.test.js:
const myService = require('./myService.js');
describe('testing myService', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('should return 2 by mocked', () => {
const service = myService();
jest.spyOn(service, 'foo').mockReturnValueOnce(2);
expect(service.foo()).toBe(2);
expect(service.foo).toBeCalledTimes(1);
});
it('should return 1', () => {
const service = myService();
expect(jest.isMockFunction(service.foo)).toBeFalsy();
expect(service.foo()).toBe(1);
});
});
test result:
PASS examples/68459394/myService.test.js (8.465 s)
testing myService
✓ should return 2 by mocked (3 ms)
✓ should return 1
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 9.548 s

Jest : mock constructor function

I'm having trouble trying to mock a constructor Function.
Here is the main class that I want to test
// main.js
import { Handler } from './handler/handler.js';
var lh = new Handler(windowAlias, documentAlias);
// rest of code
Here is how my Handler function looks. Im trying to mock this
//handler.js
export function Handler(windowAlias, documentAlias) {
this.windowAlias = windowAlias;
this.documentAlias = documentAlias;
this.attachEventListners = function(globalSet) {
// do something
};
}
And the test code:
// main.test.js
import { Handler } from 'handlers/handler'
describe('main script', () => {
it('test handler', () => {
jest.mock('handlers/handler', () => jest.fn())
const mockEventListner = jest.fn()
Handler.mockImplementation(() => ({mockEventListner}))
//call main.js
expect(mockEventListner).toBeCalledTimes(1);
})
I referred this stack overflow and tried but now Im getting error like _handler.Handler is not a constructor on the line that does new Handler(). How can I mock the new Handler call when its a constructor function
You could use jest.mock(moduleName, factory, options) to mock ./handler/handler.js module and Handler class manually.
E.g.
main.js:
import { Handler } from './handler/handler.js';
const windowAlias = 'windowAlias';
const documentAlias = 'documentAlias';
var lh = new Handler(windowAlias, documentAlias);
handler/handler.js:
export function Handler(windowAlias, documentAlias) {
this.windowAlias = windowAlias;
this.documentAlias = documentAlias;
this.attachEventListners = function(globalSet) {
// do something
};
}
main.test.js:
import './main';
import { Handler } from './handler/handler.js';
jest.mock('./handler/handler.js', () => {
return { Handler: jest.fn() };
});
describe('64382021', () => {
it('should pass', async () => {
expect(Handler).toBeCalledWith('windowAlias', 'documentAlias');
});
});
unit test result:
PASS src/stackoverflow/64382021/main.test.js
64382021
✓ should pass (6ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 4.093s, estimated 10s

Mock all except one function in a module

I am testing fileA.js, which requires fileB.js
In fileA.test.js, I wish to mock all methods from fileB.js except one.
In fileA.test.js I have:
const common = require("../src/fileB");
jest.mock("../src/fileB");
There is one method I do not wish to be mocked. Is this sort of thing possible in nodeJS?
Thank you.
You can use jest.mock and jest.requireActual(moduleName) to partial mock the methods/functions of a module.
For example:
a.js:
const b = require('./b');
exports.main = function main() {
console.log(b.method1());
console.log(b.method2());
console.log(b.method3());
};
b.js:
module.exports = {
method1: function() {
return 'method 1';
},
method2: function() {
return 'method 2';
},
method3: function() {
return 'method 3';
}
};
Now, we will mock all methods of b.js except method3.
a.spec.js:
jest.mock('./b', () => {
const originalB = jest.requireActual('./b');
const partialMockedB = Object.keys(originalB).reduce((pre, methodName) => {
pre[methodName] = jest.fn();
return pre;
}, {});
return {
...partialMockedB,
method3: originalB.method3 // mock all methods of b except method3
};
});
const { main } = require('./a');
const b = require('./b');
describe('main', () => {
test('should correct', () => {
const logSpy = jest.spyOn(console, 'log');
b.method1.mockReturnValueOnce('mocked method 1');
b.method2.mockReturnValueOnce('mocked method 2');
main();
expect(logSpy.mock.calls[0]).toEqual(['mocked method 1']);
expect(logSpy.mock.calls[1]).toEqual(['mocked method 2']);
expect(logSpy.mock.calls[2]).toEqual(['method 3']);
});
});
Unit test result:
PASS src/stackoverflow/58561765/a.spec.js
main
✓ should correct (18ms)
console.log node_modules/jest-mock/build/index.js:860
mocked method 1
console.log node_modules/jest-mock/build/index.js:860
mocked method 2
console.log node_modules/jest-mock/build/index.js:860
method 3
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.65s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58561765

How to restore a mock implementation between tests in jest?

I'm trying to create two tests for distinct functions in the same file.
I have func1 that calls func2 inside it, so I'm spying on func2 and mocking its implementation to test func1, and by the end of the first test I'm restoring the mock.
The problem is that the second test fails because func2 implementation is still mocked.
Why didn't the mock restore?
describe('myClass.func1', () => {
it('returns true', async () => {
const func2spy = jest.spyOn(myClass, 'func2').mockImplementation(() => true);
await expect(func1()).toBe(true);
func2spy.mockRestore();
})
});
describe('myClass.func2', () => {
it('returns false if argument is 0', () => {
expect(func2(0)).toBe(false);
})
});
Here is a demo works for me:
index.ts:
export class MyClass {
constructor() {}
public async func1() {
return this.func2();
}
public func2(int?: number) {
return false;
}
}
index.spec.ts:
import { MyClass } from './';
const myClass = new MyClass();
describe('MyClass.func1', () => {
it('returns true', async () => {
const func2Spy = jest.spyOn(myClass, 'func2').mockImplementation(() => true);
const actualValue = await myClass.func1();
expect(actualValue).toBeTruthy();
func2Spy.mockRestore();
});
});
describe('myClass.func2', () => {
it('returns false if argument is 0', () => {
expect(myClass.func2(0)).toBe(false);
});
});
Unit test result:
PASS src/stackoverflow/50912106/index.spec.ts
MyClass.func1
✓ returns true (7ms)
myClass.func2
✓ returns false if argument is 0 (1ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.939s, estimated 5s

Categories