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
Related
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.
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
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
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
I would like to capture the parameter passed to a stub method. And then I can perform assertion on the properties of that parameter. For Java, it is Mockito's ArgumentCaptor. Is there any equivalent thing in javascript test framework?
Here's an example:
const assert = require('chai').assert;
const expect = require('chai').expect;
const sinon = require('sinon');
const obj = {
divideByFive(a) {
return a / 5;
},
testFunc(a, b) {
return this.divideByFive(a + b) + 23;
}
};
describe('obj.testFunc()', () => {
afterEach(() => {
// Restore to the original implementation
obj.divideByFive.restore();
});
it('should call divideByFive() with the right arguments', () => {
var spy = sinon.spy(obj, 'divideByFive');
obj.testFunc(42, 1337);
assert(spy.calledWith(1379));
});
it('should return the value returned by divideByFive(), increased by 23', () => {
sinon.stub(obj, 'divideByFive').returns(1234);
expect(obj.testFunc(42, 1337)).to.equal(1257);
});
});
You can use .calledWith() (provided by Sinon) to check if a spy/stub was called with particular arguments. You should consult the documentation for more options.
Here's a standalone Mocha test to check if a spy got called with an object that had particular properties set to particular values:
const assert = require('chai').assert;
const sinon = require('sinon');
const spy = sinon.spy();
// Call the spy with an object argument.
spy({ foo : 'bar', xxx : 'yyy' });
// Check the properties.
it('should have called spy with foo:bar', function() {
assert( spy.calledWithMatch({ foo : 'bar' }) );
});
it('should have called spy with xxx:yyy', function() {
assert( spy.calledWithMatch({ xxx : 'yyy' }) );
});
it('should have called spy with xxx:zzz (WILL FAIL)', function() {
assert( spy.calledWithMatch({ xxx : 'zzz' }) );
});