How to spy on a default exported function with Jest?
Solution 1
I ended up ditching the default export:
// UniqueIdGenerator.js
export const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
And then I could use and spy it like this:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'uniqueIdGenerator');
Some recommend wrapping them in a const object, and exporting that. I suppose you can also use a class for wrapping.
However, if you can't modify the class there's still a (not-so-nice) solution:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'default');
Solution 2
one could also mock the import and pass the original implementation as mock implementation, like:
import uniqueIdGenerator from './UniqueIdGenerator'; // this import is a mock already
jest.mock('./UniqueIdGenerator.js', () => {
const original = jest. requireActual('./UniqueIdGenerator')
return {
__esModule: true,
default: jest.fn(original.default)
}
})
test(() => {
expect(uniqueIdGenerator).toHaveBeenCalled()
})
Solution 3
In some cases you have to mock the import to be able to spy the default export:
import * as fetch from 'node-fetch'
jest.mock('node-fetch', () => ({
default: jest.fn(),
}))
jest.spyOn(fetch, 'default')
Solution 4
Mock only the default export, or any other export, but keep remaining exports in module as original:
import myDefault, { myFunc, notMocked } from "./myModule";
jest.mock("./myModule", () => {
const original = jest.requireActual("./myModule");
return {
__esModule: true,
...original,
default: jest.fn(),
myFunc: jest.fn()
}
});
describe('my description', () => {
it('my test', () => {
myFunc();
myDefault();
expect(myFunct).toHaveBeenCalled();
expect(myDefault).toHaveBeenCalled();
myDefault.mockImplementation(() => 5);
expect(myDefault()).toBe(5);
expect(notMocked()).toBe("i'm not mocked!");
})
});
Solution 5
Here is a way of doing it for a default export without modifying the import (or even needing an import in the test at all):
const actual = jest.requireActual("./UniqueIdGenerator");
const spy = jest.spyOn(actual, "default");
thisismydesign
Updated on July 05, 2022Comments
-
thisismydesign almost 2 years
Suppose I have a simple file exporting a default function:
// UniqueIdGenerator.js const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8); export default uniqueIdGenerator;
Which I would use like this:
import uniqueIdGenerator from './UniqueIdGenerator'; // ... uniqueIdGenerator();
I want to assert in my test that this method was called while keeping the original functionality. I'd do that with
jest.spyOn
however, it requires an object as well as a function name as parameters. How can you do this in a clean way? There's a similar GitHub issue forjasmine
for anyone interested. -
Christopher Francisco almost 5 yearsbut that only works when it's transpiled by babel through es6 modules. Won't work on CommonJS
-
Switch386 about 3 yearsDownvoted because it doesn't address the crux of the original question - how to do this with default exports
-
AdamJB about 3 yearsDespite the fact that OP themselves opted to ditch the default export in their own accepted answer?
-
Moshe Binieli almost 3 yearsBeautiful answer, very helpful.
-
pmrotule almost 3 yearsVery nice! Personally, I still needed to have the default method returning its original value so I called it inside
jest.fn()
like:default: jest.fn((...args) => original(...args))
. -
Christopher Francisco over 2 yearsThis should be the accepted answer
-
CodingWithSpike over 2 yearsactually thanks to jest setting resetMocks:true by default now, this only works for the 1st test. After that the
default: jest.fn
gets reset and becomesundefined
, so in modern jest with the default config this no longer works either without another workaround -
agentp over 2 yearsCleanest answer for me
-
kylejw2 over 2 years
jest.mock
isn't necessary in this usecase. You could also writejest.spyOn(fetch, 'default').mockImplementation(jest.fn())
-
benmneb about 2 yearsi like
const spy = jest.spyOn(UniqueIdGenerator, 'default');
because youdont need to refactor any code and risk something happening...