How to mock const peer it in jest - javascript

There are milion questions about how to mock const inside it test, but no one works.
Can some one provide working solution how to mock import isNode from 'detect-node'; for it? (https://www.npmjs.com/package/detect-node)
My code doesnt work. Its not mocking isNode` is always true.
Example:
Test file:
import { myTestFn } from './fn'
describe(('TEST'), () => {
it(('node mock false'), () => {
jest.doMock('detect-node', () => false);
myTestFn(); // will print constant
});
it(('node mock true'), async () => {
jest.doMock('detect-node', () => true);
myTestFn(); // will print constant
});
});
Fn file:
import isNode from 'detect-node';
export const myTestFn = () => console.log({isNode})
Excepted output is:
false (for 'node mock false')
true (for 'node mock true')
I tried a lot of ways how to do it, using doMock, mock, mock in test, mock outside test,...
I am using "jest": "^26.4.2",
Thanks a lot for any help!

You should use jest.resetModules() and dynamic import('./fn') after mocking.
Resets the module registry - the cache of all required modules. This is useful to isolate modules where local state might conflict between tests.
E.g.
fn.ts:
import isNode from 'detect-node';
export const myTestFn = () => console.log({ isNode });
fn.test.ts:
describe('63811749', () => {
beforeEach(() => {
jest.resetModules();
});
it('node mock false', async () => {
jest.doMock('detect-node', () => false);
const { myTestFn } = await import('./fn');
myTestFn();
});
it('node mock true', async () => {
jest.doMock('detect-node', () => true);
const { myTestFn } = await import('./fn');
myTestFn();
});
});
unit test result:
PASS src/stackoverflow/63811749/fn.test.ts
63811749
✓ node mock false (13ms)
✓ node mock true (2ms)
console.log src/stackoverflow/63811749/fn.ts:3
{ isNode: false }
console.log src/stackoverflow/63811749/fn.ts:3
{ isNode: true }
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 4.749s, estimated 12s

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.

Mocking a method on "this" object in jest

I have the following implementation:
export const actions = {
async submitPhoneNumber(context) {
let data = await this.$axios.
$get('https://jsonplaceholder.typicode.com/todos/1')
// do something with data
return data
}
}
When I run my test I get
TypeError: Cannot read property '$get' of undefined
How do I mock this.$axios.$get?
I looked at mocking global in jest but mocking global is just mocking window.whatever.
I need to mock this object.
This is my test:
import { actions } from '#/store/channel-add'
import flushPromises from 'flush-promises'
describe('channel-add', () => {
it('submits phone number and returns phone code hash', async () => {
let data = await actions.submitPhoneNumber()
await flushPromises()
expect(data).toBeTruthy()
})
})
Here is the solution:
index.ts:
export const actions = {
// I don't know where you get $axios from this, you didn't give the completed code. so I made a fake one for the demo.
$axios: {
$get: url => ''
},
async submitPhoneNumber(context) {
let data = await this.$axios.$get('https://jsonplaceholder.typicode.com/todos/1');
// do something with data
data = this.processData(data);
return data;
},
// for demo
processData(data) {
return data;
}
};
index.spec.ts:
import { actions } from './';
actions.$axios = {
$get: jest.fn()
};
describe('actions', () => {
it('should mock action.$axios.$get method', () => {
expect(jest.isMockFunction(actions.$axios.$get)).toBeTruthy();
});
it('should get data correctly', async () => {
(actions.$axios.$get as jest.Mock<any, any>).mockResolvedValueOnce({ userId: 1 });
const actualValue = await actions.submitPhoneNumber({});
expect(actualValue).toEqual({ userId: 1 });
expect(actions.$axios.$get).toBeCalledWith('https://jsonplaceholder.typicode.com/todos/1');
});
});
Unit tests result:
PASS src/mock-module/axios/index.spec.ts
actions
✓ should mock action.$axios.$get method (4ms)
✓ should get data correctly (4ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 2.181s, estimated 3s

How to properly mock a node module for jest unit testing

I'm new to jest and just want to implement a unit test for a simple function that uses a third party node module.
function to test (say it lives in utilFolder.js):
import moduleName from 'third_party_module'
const util = {
simple_function() {
const name = moduleName.func1();
}
}
export default util;
test file:
import util from "utilFolder";
import moduleName from 'third_party_module';
jest.mock("third_party_module", () => ({
func1: jest.fn()
}));
describe("was mocked functions called", () {
test("was mocked functions called??", () => {
util.simple_function();
expect(moduleName.func1).toHaveBeenCalled();
});
});
Error:
Expected mock function to have been called, but it was not called.
Any help please?
Use jest.mock mock the module. Here is the working example:
util.js:
import moduleName from './third_party_module';
const util = {
simple_function() {
const name = moduleName.func1();
}
};
export default util;
third_party_module.js:
export default {
func1() {}
};
util.spec.js:
import util from './util';
import moduleName from './third_party_module';
jest.mock('./third_party_module', () => ({
func1: jest.fn()
}));
describe('was mocked functions called', () => {
test('was mocked functions called??', () => {
util.simple_function();
expect(moduleName.func1).toHaveBeenCalled();
});
});
Unit test result:
PASS src/stackoverflow/54729837/util.spec.js (8.414s)
was mocked functions called
✓ was mocked functions called?? (4ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 9.742s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/54729837

How do change implementation of a Jest mock for a certain test

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

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