How to properly mock a node module for jest unit testing - javascript

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

Related

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

How to mock const peer it in jest

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

Sinon Spy for Non-Class Methods

I have a javascript file with a bunch of util functions in a file called utils.js
export const processListOfItems = (input): [] => {
let listOfItems = [];
for (var index = 0; index < rawPayload.length; ++index) {
listOfItems.push(someFunction(item));
}
return listOfItems;
};
someFunction is defined in utils.js as well.
For the test, I would like to stub "someFunction," but am having trouble figuring out how to do so. It looks like sinon.spy() might be the method I want, but it looks like it requires an object, of which I don't have one since it's just a utils file.
My ideal test would look something like this
describe('someFunction fails on an item', () => {
it('returns the array with the rest of the items', () => {
const items = ['hi', 'hello'];
// I want to make it such that, when we go into the getListOfItems code, we return 42 whenever we call someFunction, rather than going into the logic itself.
const someFunctionStub = sinon.stub(someFunction).returns(42);
expect(getListOfItems(items)).toEqual([42, 42]);
});
});
sinon.stub replaces a property on an object...
...and often the object is a module and the property is a function the module exports.
When the module export of a function is stubbed, any code that calls the module export of the function will then call the stub.
It isn't possible to stub someFunction in the code above since processListOfItems isn't calling the module export of someFunction, it is calling someFunction directly.
processListOfItems needs to call the module export of someFunction in order to be able to stub the call.
Here is a simple example to demonstrate using Node.js module syntax:
util.js
exports.func1 = () => {
return 'hello ' + exports.func2(); // <= use the module
}
exports.func2 = () => 'world';
util.test.js
const sinon = require('sinon');
const util = require('./util');
describe('func1', () => {
it('should work', () => {
const stub = sinon.stub(util, 'func2').returns('everyone');
expect(util.func1()).toBe('hello everyone'); // Success!
});
});
...and here is a simple example using ES6 module syntax:
util.js
import * as util from './util'; // <= import module into itself
export const func1 = () => {
return 'hello ' + util.func2(); // <= use the module
}
export const func2 = () => 'world';
util.test.js
import * as sinon from 'sinon';
import * as util from './util';
describe('func1', () => {
it('should work', () => {
const stub = sinon.stub(util, 'func2').returns('everyone');
expect(util.func1()).toBe('hello everyone'); // Success!
});
});
Note that ES6 modules can be imported into themselves since they "support cyclic dependencies automatically".

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 do I mock a third party package with Jest?

I want to be able to test whether or not this Swal() function is called.
It's mocked but I'm not familiar with the Jest mocking library.
This is in my test set up file:
jest.mock('sweetalert2', () => {
return {
Swal: () => {},
};
});
So I just want this to return a function.
In my component, Swal is called like this:
doSomething = () => {
Swal({
title: 'Could not log in',
text: error.message,
type: 'error',
});
};
I think my mock needs to return a named method, so I can spyOn it and check that it was called.
My test:
import Swal from 'sweetalert2';
describe('Login Container', () => {
it('calls Swal', () => {
doSomething();
var swalSpy = jest.spyOn(Swal, 'Swal');
expect(swalSpy).toHaveBeenCalled();
});
});
Error:
expect(jest.fn()).tohavebeencalled();
How should I set up my mock and spy as the test fails
You can return a mock function jest.fn in your sweetalert.js mock:
module.exports = jest.fn();
And write your test like this:
import { doSomething } from './doSomething';
import Swal from 'sweetalert';
describe('Login Container', () => {
it('calls Swal', () => {
expect(Swal).toHaveBeenCalledTimes(0);
doSomething();
expect(Swal).toHaveBeenCalledTimes(1);
});
});
Note that I'm using sweetalert in my example code not sweetalert2.
Hope this help!
I would expect that the mock factory needs to return an object with default (because import Swal is importing the default module). Something like this (demoing sweetalert v1):
// extract mocked function
const mockAlert = jest.fn()
// export mocked function as default module
jest.mock('sweetalert', () => ({
default: mockAlert,
}))
// import the module that you are testing AFTER mocking
import doSomethingThatAlerts from './doSomethingThatAlerts'
// test suite loosely copied from OP
describe('Login Container', () => {
it('calls Swal', () => {
doSomethingThatAlerts();
// test mocked function here
expect(mockAlert).toHaveBeenCalled();
});
});

Categories