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();
  }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With