Reputation: 1231
I would like to strongly type my jest mocks. To a certain extent, I can make it work but when a class has private properties I'm stuck.
An additional question, when I use mocks (the way I currently do) the return type is of the original type but when I have to access any method added by Jest I have to typecast it so jest.Mock
to access a method. Is there a better way to do this? I've tried to work with jest.Mock
, jest.Mocked
, jest.MockInstance
.
If anyone could point me in the right direction that would be great!
class MyTest {
constructor(private readonly msg: string) {}
public foo(): string {
return this.msg;
}
}
const myTestMock: jest.Mock<MyTest, [string]> = jest.fn<MyTest, [string]>(() => ({
msg: 'private',
foo: jest.fn().mockReturnValue('aaa'),
}));
// Results in error:
// Type '{ msg: string; foo: Mock<any, any>; }' is not assignable to type 'MyTest'.
// Property 'msg' is private in type 'MyTest' but not in type '{ msg: string; foo: Mock<any, any>; }'
const myTestMockInstance: MyTest = new myTestMock('a');
console.log(myTestMockInstance.foo()); // --> aaa
// Accessing jest mock methods:
(<jest.Mock>myTestMockInstance).mockClear(); // <-- can this be done without type casting
Dirty workaround:
const myTestMock: jest.Mock<MyTest, [string]> = jest.fn<MyTest, [string]>(
// Cast to any to satisfy TS
(): any => ({
msg: 'private',
foo: jest.fn().mockReturnValue('aaa'),
})
);
Upvotes: 29
Views: 40827
Reputation: 648
You can use jest.fn()
to mock instantiating the class object and omit specifying the type on the variable and let Typescript infer it.
myTestMockInstance
will not have the type of MyTest
because you are mocking it.
class MyTest {
constructor(private readonly msg: string) {}
public foo(): string {
return this.msg
}
}
const myTestMock = jest.fn((msg: string) => ({
msg,
foo() {
return this.msg
}
}))
const myTestMockInstance = new myTestMock('a')
console.log(myTestMockInstance.foo()) // --> a
Upvotes: -1
Reputation: 12755
Upvotes: 2
Reputation: 303
There is a library that can help you with strongly typed mocks in Typescript with Jest: jest-mock-extended
I'm not sure you should be accessing private properties of your mock. As Kim Kern says, you should only be interested in the public interface of the dependency of the unit under test.
jest-mock-extended contains a mock()
method which returns a MockProxy
which allows you to access .mockReturnValue()
etc.
import { mock } from "jest-mock-extended";
interface WidgetService {
listMyWidgets(): { id: string }[];
};
const mockedService = mock<WidgetService>();
mockedService.listMyWidgets.mockReturnValue([{ id: 'widget-1' }]);
Upvotes: 8