How to Implement Mock With Spectator With Jest | by Ardy Gallego Dedase | Apr, 2022

Picture by Ferenc Almasi on Unsplash

I discussed in my previous blog post that Spectator is at the moment my go-to take a look at device for my Angular Apps. Up to now couple of weeks, I’ve discovered a couple of issues about mocking utilizing Spectator which I hope you’ll find helpful.

Word that I’m utilizing Spectator with Jest.

In my createServiceFactory or createComponentFactory, I’ve declared my dependencies to be mechanically mocked however I nonetheless saved the suppliers with useValue: . Within the instance under, my UserApiService is already mechanically mocked. On this case, it’s not essential to declare UserApiService within the suppliers with useValue: :

let spectator: SpectatorService<UserService>;
const createService = createServiceFactory(
service: UserService,
suppliers: [
provide: UserApiService, useValue: , // TODO: Remove, we don't need it.
],
mocks: [UserApiService], // Routinely mock
);

beforeEach(() => (spectator = createService()));

We don’t want UserApiService within the suppliers:

let spectator: SpectatorService<UserService>;
const createService = createServiceFactory(
service: UserService,
mocks: [UserApiService],
);

beforeEach(() => (spectator = createService()));

Maintaining UserApiService within the suppliers doesn’t have any side-effect. Nonetheless, it’s greatest to take away it if it’s not used.

I’ve been completely utilizing andReturn() to mock strategies inside a take a look at case since I began utilizing Spectator. I solely came upon just lately about Jest’s mockReturnValue(). It really works the identical manner as andReturn(). The primary distinction I seen is that andReturn() is just not strict with its return kind.

Given a technique that returns the kind Observable<Person>.

class UserService 
getUser(): Observable<Person>
// code right here..

The place Person is:

interface Person 
firstName: string;
lastName: string;

Utilizing mockReturnValue, I’ll get an error if I don’t mock utilizing the anticipated return kind:

const userUservice = spectator.inject(UserService);
userService.getUser.mockReturnValue(of('person mock'));

Error:

error TS2345: Argument of kind 'Observable<string>' 
is just not assignable to parameter of kind 'Observable<Person>'.

mockReturnValue() accepts the strategy’s declared return kind solely:

const userUservice = spectator.inject(UserService);
userService.getUser.mockReturnValue(of(firstName: 'First', lastName: 'Final' as Person));

Utilizing andReturn(), I can use a special kind:

const userUservice = spectator.inject(UserService);
userService.getUser.andReturn(of('person mock')); // I can mock with a string kind!

I’ve used andReturn() (and shortly mockReturnValue()) in take a look at circumstances if I care in regards to the return worth of the mock. I discover it simpler to identify after I’m on the lookout for the mocks which can be taking place in a single take a look at.

Nonetheless, there are take a look at circumstances the place I’m solely taken with asserting toHaveBeenCalled() to a mocked methodology. In that case, I’d often assign jest.fn() to the strategy that I need to assert. This solely works if the property or methodology is not read-only.

If I’m utilizing UserService in my part.

constructor(non-public userService: UserService) 

I can mock its getUser() methodology name in a take a look at by assigning the mock operate instantly. Then assert with a toHaveBeenCalled():

spectator.part['userService'].getUser = jest.fn();
//.. some code right here
count on(spectator.part['userService'].getUser).toHaveBeenCalled();

What if the strategy I need to mock is read-only?

I’ve a getter in my service:

export class UserService 
//.. some code
get canAccess$(): Observable<boolean>
// .. implementaion

I’ve a part that makes use of the above getter UserService.canAccess$. When I attempt to mock that getter utilizing andReturn():

const userService = spectator.inject(UserService);
userService.canAccess$.andReturnvalue(of(false));

I get the next error:

TS2339: Property 'andReturn' doesn't exist on kind 'Observable '.

I can also’t assign the mock instantly as a result of canAccess$ right here is read-only:

spectator.part['userService'].canAccess$ = of(false);Can't assign to 'canAccess$' as a result of it's a read-only property.

There are a couple of methods to deal with this.

Utilizing Object.defineProperty()

I beforehand used Object.defineProperty to change the service object’s property:

Object.defineProperty(spectator.part['userService'], 'canAccess$',  worth: of(true) );

This labored. I assumed there should be a approach to obtain this by utilizing Spectator reasonably than modifying the service object instantly, see the following part.

Setting useValue in a take a look at case

I came upon about this strategy after I was shopping by means of the examples in Spectator’s README.

I can declare a default mock for canAccess$ in my createComponentFactory name by setting useValue.

const createComponent = createComponentFactory(
part: MyComponent,
//...typeOrOptions right here
suppliers: [
provide: UserService, useValue: canAccess$: of(true)
],
);

All assessments in a set will use this default worth of canAccess$ until I override it inside a take a look at or one other take a look at suite. To override the default mock, I can specify the supplier within the take a look at case with a useValue. On this instance, I modify the return worth to of(false) .

const supplier = 
present: UserService,
useValue: canAccess: of(false) ,
;

Then name createComponent() utilizing the supplier with the mock override that I simply declared.

spectator = createComponent(
suppliers: [provider],
);

I can override the default mock in particular person take a look at circumstances.

it('ought to forestall entry...', () => 
const supplier =
present: UserService,
useValue: canAccess$: of(false) ,
;
spectator = createComponent(
suppliers: [provider],
);
//.. code right here
);

Or I can declare the override in beforeEach() if I need to use it in a take a look at suite:

describe('Forestall entry', () => 
beforeEach(() =>
const supplier =
present: UserService,
useValue: canAccess$: of(false) ,
;
spectator = createComponent(
suppliers: [provider],
);
//.. code right here
);
it('ought to forestall entry...', () =>
// .. code right here
);
// .. extra assessments
);

If you happen to like this story, you may also take pleasure in my different tales about Spectator Check and Angular:

More Posts