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
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 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've create a file in my test/__mocks__ folder where I mock a npm module. And the link of this file is add to my jest setup. All work great this let me test it pretty nicely. But now for a certain test I need to change the return value from this one. How can I achieve this?
I try to unMock plus setMock etc. But nothing work.
// test/__mocks__/touchId.ts
jest.mock('react-native-touch-id', () => {
return {
isSupported: jest.fn(() => Promise.resolve(true)),
authenticate: jest.fn(() => Promise.resolve(true)),
};
});
And my test
it('should not navigate to main if touch id return false', async () => {
jest.setMock('react-native-touch-id', {
authenticate: jest.fn(() => Promise.resolve(false)),
});
const pinCreation = new PinCreationStore();
const spy = jest.spyOn(NavigationServices, 'navigate');
spy.mockReset();
await pinCreation.verifyUser();
expect(spy).toHaveBeenCalledTimes(0);
spy.mockRestore();
});
Here I still get true so my test crash.
You can use jest.mock() without creating __mocks__ folder.
For example:
react-native-touch-id.ts, I simulate this module in order to keep it simple. You can replace it with real npm module.
const touchId = {
isSupported() {
return false;
},
authenticate() {
return false;
}
};
export default touchId;
react-native-touch-id.spec.ts:
import touchId from './react-native-touch-id';
jest.mock('./react-native-touch-id', () => {
return {
isSupported: jest.fn(() => Promise.resolve(true)),
authenticate: jest.fn(() => Promise.resolve(true))
};
});
describe('react-native-touch-id', () => {
it('t1', () => {
expect(touchId.isSupported()).toBeTruthy();
expect(touchId.authenticate()).toBeTruthy();
});
it('t2', () => {
(touchId.isSupported as jest.MockedFunction<typeof touchId.isSupported>).mockReturnValueOnce(false);
(touchId.authenticate as jest.MockedFunction<typeof touchId.authenticate>).mockReturnValueOnce(false);
expect(touchId.isSupported()).toBeFalsy();
expect(touchId.authenticate()).toBeFalsy();
});
it('t3', () => {
(touchId.isSupported as jest.MockedFunction<typeof touchId.isSupported>).mockReturnValueOnce(true);
(touchId.authenticate as jest.MockedFunction<typeof touchId.authenticate>).mockReturnValueOnce(false);
expect(touchId.isSupported()).toBeTruthy();
expect(touchId.authenticate()).toBeFalsy();
});
});
As you can see, after you mock react-native-touch-id module, you need to import it and mocked again when you want these two methods to have different values. These mocked values will be used in other modules which import and use isSupported and authenticate methods of react-native-touch-id module.
Unit test result:
PASS src/stackoverflow/52172531/react-native-touch-id.spec.ts
react-native-touch-id
✓ t1 (5ms)
✓ t2 (1ms)
✓ t3
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 4.065s
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 have a function that I want to test and this function uses an imported module:
var a = require('./a');
function add(b) {
return a + b;
}
module.exports = add;
That a module returns a number in this sample, but in my real project I use that as a config object that is changed from time to time manually.
var a = 1;
module.exports = a;
The test for the add function looks like this:
describe('add', () => {
it('should add the mock number 1 to 2', () => {
jest.setMock('./a', 1);
const add = require('./add');
expect(add(2)).toBe(3);
});
it('should add the mock number 2 to 2', () => {
jest.setMock('./a', 2);
const add = require('./add');
expect(add(2)).toBe(4);
});
});
First test passes, The second test fails because it inherits from the first mock. Is there any way to mock the a module multiple times?
I would like a solution that doesn't imply refactoring the add function and instead focus on mocking that module multiple times. (in my real project that is a config file)
You can play around with the code here: https://repl.it/#adyz/NocturnalBadComma
Add
beforeEach(() => {
jest.resetModules();
});
Final tests
describe('add', () => {
beforeEach(() => {
jest.resetModules();
});
it('should add the mock number 5 to 2', () => {
jest.setMock('./a', 5);
const add = require('./add');
expect(add(2)).toBe(7);
});
it('should add the mock number 2 to 2', () => {
jest.setMock('./a', 2);
const add = require('./add');
expect(add(2)).toBe(4);
});
});
Demo: https://repl.it/repls/TrustingBelatedProprietarysoftware
To add to #Gigi's solution, I created another example, using jest.mock:
In the file multiplier.ts, multiplier is the exported function we want to test:
// file: multiplier.ts
import {getNumber} from './get-number'
const multiplier = (num:number) => num * getNumber()
export {multiplier}
In the file get-number.ts, getNumber is the module we want to mock:
// file: get-number.ts
const getNumber = () => 2
export {getNumber}
Here is the test:
// file: multiplier.test.ts
// import { multiplier } from "./multiplier" // <-- this will not work
describe("[multiplier]", () => {
beforeEach(() => {
jest.resetModules()
})
it('should mock getNumber so that getNumber return 3', () => {
const mockReturn = 3
jest.mock( './get-number', () => (
{ getNumber: jest.fn(()=>mockReturn) }
))
const { multiplier } = require('./multiplier')
expect(multiplier(2)).toBe(6)
})
it('should mock getNumber so that getNumber return 4', () => {
const mockReturn = 4
jest.mock( './get-number', () => (
{ getNumber: jest.fn(()=>mockReturn) }
))
const { multiplier } = require('./multiplier')
expect(multiplier(2)).toBe(8)
})
it('should mock getNumber so that getNumber return 5', () => {
const mockReturn = 5
jest.mock( './get-number', () => (
{ getNumber: jest.fn(()=>mockReturn) }
))
const { multiplier } = require('./multiplier')
expect(multiplier(2)).toBe(10)
})
})
Note: for this to work, we need to use require to import multiplier.ts
For callback functions, working approach is-
const { validate } = require("../../controllers/..")
describe('Party Authentication', () => {
afterEach(() => { jest.resetModules(); });
it('should return success response', async () => {
let req = {}
req['headers'] = {}
req['body'] = {}
jest.mock('../../config/database', () => ({
execute: (param1, param2, param3, callback) => callback(null, [{ return: 1, party_guid: "20090911093921694613", policy_guid: '20090911093921422222' }])
}));
const data = await validate(req)
expect(data.status).toBe(true)
expect(data).toHaveProperty('insuredGuid')
expect(data.insuredGuid).toBeTruthy()
expect(data).toHaveProperty('policyGuidAuthentication')
expect(data.policyGuidAuthentication).toBeTruthy()
})
it('should return response with error code and message', async () => {
let req = {}
req['headers'] = {}
req['body'] = {}
jest.mock('../../config/database', () => ({
execute: (param1, param2, param3, callback) => callback(null, [{ return: 0, error_message: "Segurado não foi localizado com a Guid informada", error_code: 5684 }])
}));
const data = await validate(req)
console.log("datadatadatadatadata", data)
expect(data.status).toBe(false)
expect(data).toHaveProperty('error')
expect(data.error).toBeTruthy()
})
})