Mocking in Typescript unit tests
As pointed out by @Terite any
type on mocks is poor choice as there would be no relationship between mock and its actual type / implementation. So improved solution may be casting partially-mocked object to mocks type:
export interface UserService {
getUser: (id: number) => User;
saveUser: (user: User) => void;
// ... number of other methods / fields
}
.......
let userServiceMock: UserService = <UserService> {
saveUser(user: User) { console.log("save user"); }
}
spyOn(userServiceMock, 'getUser').andReturn(new User());
expect(userServiceMock.getUser).toHaveBeenCalledWith(expectedUserId);
It also worth mention that Typescript won't allow to cast any object that has extra members (superset or derived type). Means that your partial mock is actually of base type to the UserService
and can be safely cast. e.g.
// Error: Neither type '...' nor 'UserService' is assignable to the other.
let userServiceMock: UserService = <UserService> {
saveUser(user: User) { console.log("save user"); },
extraFunc: () => { } // not available in UserService
}
Now that TypeScript 3 is out, full strong typing can finally be expressed! I took advantage of this and ported NSubstitute to TypeScript.
It can be found here: https://www.npmjs.com/package/@fluffy-spoon/substitute
I made a comparison versus most popular frameworks here: https://medium.com/@mathiaslykkegaardlorenzen/with-typescript-3-and-substitute-js-you-are-already-missing-out-when-mocking-or-faking-a3b3240c4607
Notice how it can create fakes from interfaces, and have full strong typing along the way!
My experience with unit tests in TypeScript definitely shows that it's worth to keep all mock objects typed. When you leave your mocks with a type of any
, it becomes problematic during a rename. The IDE won't correctly discover which occurrences of the user
or settings
param should be changed. Of course, writing mock objects manually with a complete interface is really laborious.
Fortunately, there are two tools for TypeScript that allows creating type-safe mock objects: ts-mockito (inspired by Java mockito) and typemoq (inspired by C# Moq).