jest typescript property mock does not exist on type
When using jest.fn()
to add a mock you can usually access the .mock
property to access details such as calls, something similar to this:
test('not working', () => {
const foo = new Foo();
foo.addListener = jest.fn();
foo.func(); // will call addListener with a callback
const callback = foo.addListener.mock.calls[0][0];
expect(callback()).toEqual(1); // test the callback
});
When implementing the test in typescript instead of plain javascript I get the error:
error TS2339: Property 'mock' does not exist on type '(callback: () => number) => void'.
I can get rid of the error by casting to any
but surely there must be a better way:
const callback = (foo.addListener as any).mock.calls[0][0];
In this simple code the mock could be rewritten to store the argument using jest.fn(fn => { callback = fn; });
but the same error happens when using foo.addListener.mockClear()
which cannot be reworked the same way.
So how can I get rid of the error, preferably without losing type-safety?
Solution 1:
For anyone getting here, a bit better than casting to any
might be casting as jest.Mock
const callback = (foo.addListener as jest.Mock).mock.calls[0][0];
Update Sep 2021
To get a mocked function that both fulfills the mocked function type and the jest mock type jest.MockedFunction
can be used:
const addListenerMock = addListener as jest.MockedFunction<typeof addListener>;
Solution 2:
You can use jest.spyOn
in combination with functions like mockImplementation
to mock a function while preserving type safety in TypeScript:
class Foo {
addListener = (callback: () => number) => { }
func = () => {
this.addListener(() => 1);
}
}
test('working', () => {
const foo = new Foo();
const mockAddListener = jest.spyOn(foo, 'addListener'); // spy on foo.addListener
mockAddListener.mockImplementation(() => { }); // replace the implementation if desired
foo.func(); // will call addListener with a callback
const callback = mockAddListener.mock.calls[0][0];
expect(callback()).toEqual(1); // SUCCESS
});
Solution 3:
Got the error below when using axios
.
TS2339 (TS) Property 'mockResolvedValueOnce' does not exist on type 'AxiosStatic'
Tried using axios as jest.Mock
but got the error below:
TS2352 (TS) Conversion of type 'AxiosStatic' to type 'Mock<any, any>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type 'AxiosStatic' is missing the following properties from type 'Mock<any, any>': getMockName, mock, mockClear, mockReset, and 12 more.
Solved it by specifying as axios as unknown as jest.Mock
AxiosRequest.test.tsx
import axios from 'axios';
import { MediaByIdentifier } from '../api/mediaController';
jest.mock('axios', () => jest.fn());
test('Test AxiosRequest',async () => {
const mRes = { status: 200, data: 'fake data' };
(axios as unknown as jest.Mock).mockResolvedValueOnce(mRes);
const mock = await MediaByIdentifier('Test');
expect(mock).toEqual(mRes);
expect(axios).toHaveBeenCalledTimes(1);
});
mediaController.ts:
import { sendRequest } from './request'
import { AxiosPromise } from 'axios'
import { MediaDto } from './../model/typegen/mediaDto';
const path = '/api/media/'
export const MediaByIdentifier = (identifier: string): AxiosPromise<MediaDto> => {
return sendRequest(path + 'MediaByIdentifier?identifier=' + identifier, 'get');
}
request.ts:
import axios, { AxiosPromise, AxiosRequestConfig, Method } from 'axios';
const getConfig = (url: string, method: Method, params?: any, data?: any) => {
const config: AxiosRequestConfig = {
url: url,
method: method,
responseType: 'json',
params: params,
data: data,
headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/json' },
}
return config;
}
export const sendRequest = (url: string, method: Method, params?: any, data?: any): AxiosPromise<any> => {
return axios(getConfig(url, method, params, data))
}