Typescript is a great language that allows Javascript developers be more productive by leveraging type safety. However when working with Jasmine tests you lose this benefit as in most cases you have to use type any .

I’m going to use Angular example, however the scenario is applicable for any other framework.

The problem

Let’s say we have a service:

To test this service with Jasmine you would usually write a setup code similar to this:

The first problem is on line 3: let http: any . It means all type safety has gone, no more code suggestions from your beloved IDE.

Add type safety to mocks

Luckily resolving the first problem is trivial. Declare a mapped type:

export type Spied<T> = {

[Method in keyof T]: jasmine.Spy;

};

and change from any to Spied<HttpClient> :

Now you get back typing information about the service and moreover about jasmine spy as well:

Detailed information about the approach can be found in Vildan Softic blogpost Type-safe Jasmine Spies.

Simplify jamsine.createSpyObj() function

The second problem with the test is on line 11:

useValue: jasmine.createSpyObj('http', ['get'])

If in the future you add a method which uses POST action, you will have to come back and add post to the declaration. It is very annoying and error prone. For example, a method names is updated during refactoring, but your spy will remain the same and as a result your tests will fail.

I’d love to have a method spyOnClass which accepts just a class type, and a spy with all method as a returned value, e.g. useValue: spyOnClass(HttpClient) .

Here is the implementation:

I’ve added some comments to the code, but the idea is simple: we get all props and methods of the class prototype, filter methods and create a spy object with those methods. Now we can create a spy for any class, we don’t need to care about spelling errors in methods, renaming/refactoring is supported as well 😍!

Make it even better

Now we can mock a class as:

{ provide: HttpClient, useValue: spyOnClass(HttpClient) }

This type of code will be repeated everywhere, let’s move it to a function as well:

export function provideMock<T>(spiedClass: Type<T>) {

return {

provide: spiedClass,

useValue: spyOnClass(spiedClass)

};

}

Now the setup becomes even simpler:

Here we are, our spies now have typing information and the spy creation itself is simple and generic. Below is a full working Stackblitz example:

Please 👏 if you find this information useful. The technique above has already helped a lot on our current project.