I've an angular web app. I'm trying to test some services, that have other services as depenency. I need to mock those others services to test my service. I'm using Jest+auto-jest-spies to mock my classes, but I'm open to other suggestions.
Here is an example of class that I'm trying to mock(a store made with signalstory) :
import { computed, Injectable } from '@angular/core';
import { ImmutableStore, useDevtools } from 'signalstory';
import { AccountStateModel } from './account-state.model';
@Injectable({ providedIn: 'root' })
export class AccountStore extends ImmutableStore<AccountStateModel> {
constructor() {
super({
plugins: [useDevtools()],
initialState: {
isInitialized: false,
email: null,
accessToken: null,
name: null,
tokenExpiration: null,
userId: null,
permissions: null,
},
});
}
//Queries
public get isLoggedIn() {
return computed(() => !!this.state().userId);
}
public get userInfo() {
return this.state;
}
// Commands
//...
}
I'm trying to mock it like this:
describe('PermissionGuard', () => {
let storeMock!: Spy<AccountStore>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{ provide: AccountStore, useValue: createSpyFromClass(AccountStore, { gettersToSpyOn: ['isLoggedIn'] }) },
PermissionGuard,
],
}).compileComponents();
storeMock = TestBed.inject<any>(AccountStore);
});
it('should test the user access', async () => {
//Arrange
storeMock.isLoggedIn.mockReturnValue(false);
//Act
//...
//Assert
//...
});
});
But the isLoggedIn
is not recognized as a getter, I guess because it's a getter of a "function"(signal).
So what can I do to mock this class? I also want to make sure a signal has been accessed.
Essentially, the problem is that the getter function returns a signal, which is by itself a function. To keep all the characteristics of the signal intact, you could do something like this instead:
describe('PermissionGuard', () => {
let storeMock: Spy<AccountStore>;
let permissionGuard: PermissionGuard;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: AccountStore,
useValue: createSpyFromClass(AccountStore, {
methodsToSpyOn: ['isLoggedIn'],
}),
},
PermissionGuard,
],
}).compileComponents();
storeMock = TestBed.inject(AccountStore) as Spy<AccountStore>;
permissionGuard = TestBed.inject(PermissionGuard);
});
it('should test the user access', () => {
//Arrange
storeMock.isLoggedIn.mockImplementation(signal(true));
//Act
const result = permissionGuard.isValid();
//Assert
expect(result).toBe(true);
expect(storeMock.isLoggedIn).toHaveBeenCalledTimes(1);
expect(storeMock.isLoggedIn).toHaveBeenCalledWith();
});
});
Assuming here that PermissionGuard is defined like
@Injectable({ providedIn: 'root' })
export class PermissionGuard {
constructor(private readonly account: AccountStore) {}
isValid() {
return this.account.isLoggedIn();
}
}