I'm creating a custom mock (of an ES6 class) with Jest in a Typescript project. The mock creates end exports a few mock.fn()
so that they could be spied on in the test suite.
An example could be the official one from the Jest documentation (https://jestjs.io/docs/en/es6-class-mocks#manual-mock). There the SoundPlayer
class has been mocked, as it is its only method playSoundFile
. The method is mocked using a jest.fn()
, which is exported to be used in tests.
// soundPlayer.ts
export default class SoundPlayer {
foo: string = 'bar';
playSoundFile(filename: string) {
console.log(`Playing sound file ${filename}`);
}
}
// __mocks__/soundPlayer.ts
export const mockPlaySoundFile = jest.fn();
const mock = jest.fn().mockImplementation(() => {
return { playSoundFile: mockPlaySoundFile };
});
export default mock;
// __tests__/soundPlayer.ts
import SoundPlayer, { mockPlaySoundFile } from '../soundPlayer';
jest.mock('../soundPlayer');
beforeEach(() => {
mockPlaySoundFile.mockClear();
});
it('is called with filename', () => {
const filename = 'song.mp3';
const soundPlayer = new SoundPlayer();
soundPlayer.playSoundFile(filename);
expect(mockPlaySoundFile).toBeCalledWith(filename);
});
The test works as expected, but TS notifies an error (which kind of makes sense to me) when trying to import the mocked mockPlaySoundFile
function. That is because, obviously, mockPlaySoundFile
doesn't exist in soundPlayer.ts
. But because of jest.mock('../soundPlayer');
the mock is imported under the hood, therefore the export does exist.
Is there a way to inform TS to look at the mocks in cases like this?
Update 29 Sept 2022:
The mocked
function has been integrated into jest and is not available in ts-jest anymore.
Original answer:
The easiest way to fix this is to use ts-jest's mocked()
helper. The helper will make sure you have access to the mock test methods. __tests__/soundPlayer.ts
would then look as follows:
// __tests__/soundPlayer.ts
import { mocked } from "ts-jest/utils";
import SoundPlayer from '../soundPlayer';
jest.mock('../soundPlayer');
const soundPlayer = mocked(new SoundPlayer());
beforeEach(() => {
soundPlayer.playSoundFile.mockClear();
});
it('is called with filename', () => {
const filename = 'song.mp3';
soundPlayer.playSoundFile(filename);
expect(soundPlayer.playSoundFile).toBeCalledWith(filename);
});
If you really want to include mockPlaySoundFile
you could do it by telling the Typescript compiler to suppresses the import error:
// @ts-ignore
import { mockPlaySoundFile } from '../soundPlayer';
Also, have a look at the example in my repo here: https://github.com/tbinna/ts-jest-mock-examples, in particular, your soundPlayer
example: https://github.com/tbinna/ts-jest-mock-examples/tree/master/sound-player
I have the same problem and I only have a workaround. My problem is that I manually mock fs from node.
So I have a manual mock for 'fs' which looks roughly like this:
const fs = jest.genMockFromModule("fs");
let mockFiles = {};
function __clearMocks(){
mockFiles = {};
}
module.exports = fs;
So clearly when my test case imports fs it would not work:
import * as fs from 'fs';
fs.__clearMocks();
To get this working I create an extension of the type:
declare module 'fs' {
export function __clearMocks(): void;
}
So now I can modify my testcase like this:
import * as fs from 'fs';
import 'fsExtension';
fs.__clearMocks();
Hopefully this helps you!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With