What I discovered about Mocking with Spectator Exams

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: