Jest how to test if a mocked function is called - unit-testing

In my jest test I mock a service that i have made like this:
jest.mock("../../services/testService");
this works fine. Now I want to test if a mocked function was called how can is do this?
It tried to put the mock in a variable like this:
const fn = jest.mock("../../services/testService");
But when i do this i get errors because the test now refer to the real testservice and not the mocked one.
Can someone tell me how to test if a mocked function was called?

You could do something like
//this is the key to know if a function has been called
const yourFunction = jest.fn()
jest.doMock('../../services/testService', () => ({
testService: {
yourFunction
},
}))
And then use the assertion on yourFunction to have been called.
EDIT:
I think this should work if you are trying to mock react-native-push-notification:
const localNotification = jest.fn()
jest.doMock('react-native-push-notification', () => ({
PushNotification: {
configure: () => {},
default: () => {},
localNotification
},
}))

Related

How to assert a default exported function is called when mocking a module in Jest?

I'm trying to mock an external dependency in my Jest test.
My goal is to validate that the dependency was called with some specified arguments.
The dependency exports a default function, which makes it hard (impossible?) to use jest.SpyOn.
I've tried using jest.mock as follows:
const callback = jest.fn(() => 'output');
jest.mock('dependecy', () => callback);
it('call dep with some args' () => {
...
expect(callback).toBeCalledWith('arg1' , 'arg2')
});
Also tried jest.mock('dependecy', () => ({ default: callback }));.
But neither where successful, I was thinking this was because of the hoisting of jest.mock. Thus I've tried jest.doMock, but this does not even seem to call/create my mock.
Any ideas on how to validate that a method was called on an external dependency that exports a default function in jest?
Here is a simple working example that should get you going:
code.js
import dependency from 'dependency';
export const func = () => 'returned ' + dependency();
code.test.js
import { func } from './code';
import dependency from 'dependency'; // <= dependency will be...
jest.mock('dependency', () =>
jest.fn(() => 'mocked') // <= ...this mock function
);
describe('func', () => {
it('should call dependency', () => {
const result = func();
expect(result).toBe('returned mocked'); // Success!
expect(dependency).toHaveBeenCalled(); // Success!
});
});
Note that the module factory function must be self-contained so it can be hoisted by babel-jest.

use jest mock functions to test code that calls another server

Roughly, my JavaScript function that I want to unit-test looks like this:
const request = require('request-promise-native');
async function callServer() {
// Prepare parameters
// Call `request` with parameters
// Parse response JSON and return
}
Is there any way to unit-test this function without making an actual call to the real server? Can I use a jest mock function to somehow override request()? To do that, will I need to modify every function to take the request function as a parameter or there is another way?
You can mock imported module via jest.mock. https://jestjs.io/docs/en/api#requirerequiremockmodulename
describe('main description', () => {
it('description of test case', () => {
jest.mock('request-promise-native', () => {
return {}; // Return what the request-promise-native supposed to return
});
const result = callServer();
expect(result).toBe({});
});
});

How do I mock an exported typescript function in a jasmine test?

I'm trying to mock a function exported from a typescript file in a Jasmine test. I expect the following to mock the imported foo and return the value 1 in the spec for bar.
The mock appears to be uncalled, so I'm clearly missing something. How can I fix this example?
demo.ts:
export function foo(input: any): any {
return 2;
}
export function bar(input: any): any {
return foo(input) + 2;
}
demo.ts.spec:
import * as demo from './demo';
describe('foo:', () => {
it('returns 2', () => {
const actual = demo.foo(1);
expect(actual).toEqual(2);
});
});
describe('bar:', () => {
// let fooSpy;
beforeEach(() => {
spyOn(demo, 'foo' as any).and.returnValue(1); // 'as any' prevents compiler warning
});
it('verifies that foo was called', () => {
const actual = demo.bar(1);
expect(actual).toEqual(3); // mocked 1 + actual 2
expect(demo.foo).toHaveBeenCalled();
});
});
Failures:
Expected 4 to equal 3.
Expected spy foo to have been called.
Jeffery's answer helped get me on the right track.
To attach the spy is attached to the right reference for foo the product code needs to have a small change. foo() should be called as this.foo()
The below pattern works for testing (and is a lot cleaner than the convoluted work around I was using previously).
demo.ts:
export function foo(input: any): any {
return 2;
}
export function bar(input: any): any {
return this.foo(input) + 2;
}
demo.ts.spec:
import * as demo from './demo';
describe('foo:', () => {
it('returns 2', () => {
const actual = demo.foo(1);
expect(actual).toEqual(2);
});
});
describe('bar:', () => {
// let fooSpy;
beforeEach(() => {
spyOn(demo, 'foo' as any).and.returnValue(1);
});
it('verifies that foo was called', () => {
const actual = demo.bar(1);
expect(actual).toEqual(3);
expect(demo.foo).toHaveBeenCalled();
});
});
From this issue on Github: How are you expecting to use the spied on function in your actual implementation?
Your bar implementation calls the actual implementation of foo, because it has a direct reference to it. When importing in another module, a new object, with new references, is created:
// This creates a new object { foo: ..., bar: ... }
import * as demo from './demo';
These references exist only in the module of the import. When you call spyOn(demo, 'foo') it's that reference that is being used. You might want to try this in your spec, chances are the test passes:
demo.foo();
expect(demo.foo).toHaveBeenCalled();
Expecting the real implementation of bar to call a mocked foo is not really possible. Instead try to treat bar as if it had its own implementation of foo.

Test that private function defined in a module is called with correct args by public function from the same module (using Jest)

I have a project that I am writing Jest tests for. I have a particular module that is defined like this:
export default function main(arg) {
/* do some stuff with arg */
sub(arg);
}
function sub(arg) {
return;
}
I'd like to test that main calls sub with the correct arguments. I tried this:
import main from './tiny';
describe('main function', () => {
it('calls sub with correct arguments', () => {
let sub = jest.fn();
main('hello world');
expect(sub).toHaveBeenCalled();
expect(sub).toHaveBeenCalledWith('hello world');
});
});
But this test always fails with:
main function › calls sub with correct arguments
expect(jest.fn()).toHaveBeenCalled()
I'm new to unit testing and mocking, so I'm sure this is easy, but I am totally stuck. Please help!
In case anyone finds this, I made it work with the following (somewhat ugly) mods:
Export the function and call it from exports:
export default function main(arg) {
/* do some stuff with arg */
exports.sub(arg);
}
export function sub(arg) {
return;
}
Import the exports object an overwrite the method with a module:
import main, * as tiny from './tiny';
describe('main function', () => {
it('calls sub with correct arguments', () => {
tiny.sub = jest.fn();
main('hello world');
expect(tiny.sub).toHaveBeenCalled();
expect(tiny.sub).toHaveBeenCalledWith('hello world');
});
});

How to mock imported named function in Jest when module is unmocked

I have the following module I'm trying to test in Jest:
// myModule.js
export function otherFn() {
console.log('do something');
}
export function testFn() {
otherFn();
// do other things
}
As shown above, it exports some named functions and importantly testFn uses otherFn.
In Jest when I'm writing my unit test for testFn, I want to mock the otherFn function because I don't want errors in otherFn to affect my unit test for testFn. My issue is that I'm not sure the best way to do that:
// myModule.test.js
jest.unmock('myModule');
import { testFn, otherFn } from 'myModule';
describe('test category', () => {
it('tests something about testFn', () => {
// I want to mock "otherFn" here but can't reassign
// a.k.a. can't do otherFn = jest.fn()
});
});
Any help/insight is appreciated.
Use jest.requireActual() inside jest.mock()
jest.requireActual(moduleName)
Returns the actual module instead of a mock, bypassing all checks on whether the module should receive a mock implementation or not.
Example
I prefer this concise usage where you require and spread within the returned object:
// myModule.test.js
import { otherFn } from './myModule.js'
jest.mock('./myModule.js', () => ({
...(jest.requireActual('./myModule.js')),
otherFn: jest.fn()
}))
describe('test category', () => {
it('tests something about otherFn', () => {
otherFn.mockReturnValue('foo')
expect(otherFn()).toBe('foo')
})
})
This method is also referenced in Jest's Manual Mocks documentation (near the end of Examples):
To ensure that a manual mock and its real implementation stay in sync, it might be useful to require the real module using jest.requireActual(moduleName) in your manual mock and amending it with mock functions before exporting it.
Looks like I'm late to this party, but yes, this is possible.
testFn just needs to call otherFn using the module.
If testFn uses the module to call otherFn then the module export for otherFn can be mocked and testFn will call the mock.
Here is a working example:
myModule.js
import * as myModule from './myModule'; // import myModule into itself
export function otherFn() {
return 'original value';
}
export function testFn() {
const result = myModule.otherFn(); // call otherFn using the module
// do other things
return result;
}
myModule.test.js
import * as myModule from './myModule';
describe('test category', () => {
it('tests something about testFn', () => {
const mock = jest.spyOn(myModule, 'otherFn'); // spy on otherFn
mock.mockReturnValue('mocked value'); // mock the return value
expect(myModule.testFn()).toBe('mocked value'); // SUCCESS
mock.mockRestore(); // restore otherFn
});
});
import m from '../myModule';
Does not works for me, I did use:
import * as m from '../myModule';
m.otherFn = jest.fn();
I know this was asked a long time ago, but I just ran into this very situation and finally found a solution that would work. So I thought I'd share here.
For the module:
// myModule.js
export function otherFn() {
console.log('do something');
}
export function testFn() {
otherFn();
// do other things
}
You can change to the following:
// myModule.js
export const otherFn = () => {
console.log('do something');
}
export const testFn = () => {
otherFn();
// do other things
}
exporting them as a constants instead of functions. I believe the issue has to do with hoisting in JavaScript and using const prevents that behaviour.
Then in your test you can have something like the following:
import * as myModule from 'myModule';
describe('...', () => {
jest.spyOn(myModule, 'otherFn').mockReturnValue('what ever you want to return');
// or
myModule.otherFn = jest.fn(() => {
// your mock implementation
});
});
Your mocks should now work as you would normally expect.
The transpiled code will not allow babel to retrieve the binding that otherFn() is referring to. If you use a function expession, you should be able to achieve mocking otherFn().
// myModule.js
exports.otherFn = () => {
console.log('do something');
}
exports.testFn = () => {
exports.otherFn();
// do other things
}
// myModule.test.js
import m from '../myModule';
m.otherFn = jest.fn();
But as #kentcdodds mentioned in the previous comment, you probably would not want to mock otherFn(). Rather, just write a new spec for otherFn() and mock any necessary calls it is making.
So for example, if otherFn() is making an http request...
// myModule.js
exports.otherFn = () => {
http.get('http://some-api.com', (res) => {
// handle stuff
});
};
Here, you would want to mock http.get and update your assertions based on your mocked implementations.
// myModule.test.js
jest.mock('http', () => ({
get: jest.fn(() => {
console.log('test');
}),
}));
Basing on Brian Adams' answer this is how I was able to use the same approach in TypeScript. Moreover, using jest.doMock() it's possible to mock module functions only in some specific tests of a test file and provide an individual mock implementations for each of them.
src/module.ts
import * as module from './module';
function foo(): string {
return `foo${module.bar()}`;
}
function bar(): string {
return 'bar';
}
export { foo, bar };
test/module.test.ts
import { mockModulePartially } from './helpers';
import * as module from '../src/module';
const { foo } = module;
describe('test suite', () => {
beforeEach(function() {
jest.resetModules();
});
it('do not mock bar 1', async() => {
expect(foo()).toEqual('foobar');
});
it('mock bar', async() => {
mockModulePartially('../src/module', () => ({
bar: jest.fn().mockImplementation(() => 'BAR')
}));
const module = await import('../src/module');
const { foo } = module;
expect(foo()).toEqual('fooBAR');
});
it('do not mock bar 2', async() => {
expect(foo()).toEqual('foobar');
});
});
test/helpers.ts
export function mockModulePartially(
modulePath: string,
mocksCreator: (originalModule: any) => Record<string, any>
): void {
const testRelativePath = path.relative(path.dirname(expect.getState().testPath), __dirname);
const fixedModulePath = path.relative(testRelativePath, modulePath);
jest.doMock(fixedModulePath, () => {
const originalModule = jest.requireActual(fixedModulePath);
return { ...originalModule, ...mocksCreator(originalModule) };
});
}
Mocking functions of a module is moved to helper function mockModulePartially located in a separate file so it can be used from different test files (which, in common, can be located in other directories). It relies on expect.getState().testPath to fix path to a module (modulePath) being mocked (make it relative to helpers.ts containing mockModulePartially). mocksCreator function passed as a second argument to mockModulePartially should return mocks of the module. This function receives originalModule and mock implementations can optionally rely on it.
I solved my problem with a mix of the answers that I found here:
myModule.js
import * as myModule from './myModule'; // import myModule into itself
export function otherFn() {
return 'original value';
}
export function testFn() {
const result = myModule.otherFn(); // call otherFn using the module
// do other things
return result;
}
myModule.test.js
import * as myModule from './myModule';
describe('test category', () => {
let otherFnOrig;
beforeAll(() => {
otherFnOrig = myModule.otherFn;
myModule.otherFn = jest.fn();
});
afterAll(() => {
myModule.otherFn = otherFnOrig;
});
it('tests something about testFn', () => {
// using mock to make the tests
});
});
On top of the first answer here, you can use babel-plugin-rewire to mock imported named function too. You can check out the section superficially for
named function rewiring.
One of the immediate benefits for your situation here is that you do not need to change how you call the other function from your function.