What’s the best way to correctly mock the following example?
The problem is that after import time, foo
keeps the reference to the original unmocked bar
.
module.js
:
export function bar () { return 'bar'; } export function foo () { return `I am foo. bar is ${bar()}`; }
module.test.js
:
import * as module from '../src/module'; describe('module', () => { let barSpy; beforeEach(() => { barSpy = jest.spyOn( module, 'bar' ).mockImplementation(jest.fn()); }); afterEach(() => { barSpy.mockRestore(); }); it('foo', () => { console.log(jest.isMockFunction(module.bar)); // outputs true module.bar.mockReturnValue('fake bar'); console.log(module.bar()); // outputs 'fake bar'; expect(module.foo()).toEqual('I am foo. bar is fake bar'); /** * does not work! we get the following: * * Expected value to equal: * "I am foo. bar is fake bar" * Received: * "I am foo. bar is bar" */ }); });
I could change:
export function foo () { return `I am foo. bar is ${bar()}`; }
to:
export function foo () { return `I am foo. bar is ${exports.bar()}`; }
but this is pretty ugly in my opinion to do everywhere.
Advertisement
Answer
fwiw, the solution I settled on was to use dependency injection, by setting a default argument.
So I would change
export function bar () { return 'bar'; } export function foo () { return `I am foo. bar is ${bar()}`; }
to
export function bar () { return 'bar'; } export function foo (_bar = bar) { return `I am foo. bar is ${_bar()}`; }
This is not a breaking change to the API of my component, and I can easily override bar in my test by doing the following
import { foo, bar } from '../src/module'; describe('module', () => { it('foo', () => { const dummyBar = jest.fn().mockReturnValue('fake bar'); expect(foo(dummyBar)).toEqual('I am foo. bar is fake bar'); }); });
This has the benefit of leading to slightly nicer test code too 🙂