SpyOn private property which is a class with methods

185 Views Asked by At

My Angular app uses the oidc-client UserManager class to manage OAuth authentication.

I have a service as below

export class AuthService {
  private userManager: UserManager
  private readonly configService: ConfigService;
  constructor(configService: ConfigService) {
    this.configService = configService;
  }
...
  async load(): Promise<any> {
    config = this.configService.getConfig();
    this.userManager = new UserManager(config);
    const user = await this.userManager.getUser();
...

And my spec file setup is as below:

  beforeEach(() => {
    const spy = jasmine.createSpyObj('UserManager', ['getUser']);
    spy.getUser.and.returnValue(mockUser);
    const configSpy = jasmine.createSpyObj('ConfigService', ['getConfig']);
    configSpy.getConfig.and.returnValue(mockConfig);

    TestBed.configureTestingModule({
      providers: [
        AuthenticationService,
        { provide: UserManager, useValue: spy },
        { provide: AppConfigService, useValue: configSpy }
      ]
    });
    authService = TestBed.inject(AuthenticationService);
    appConfigSpy = TestBed.inject(ConfigService) as jasmine.SpyObj<ConfigService>;
    userManagerSpy = TestBed.inject(UserManager) as jasmine.SpyObj<UserManager>;
  });

...and my first test case is :

    it('should initialise the user manager', async () => {
      // arrange 
      userManagerSpy.getUser.and.resolveTo(mockUser);
      appConfigSpy.getConfig.and.returnValue(mockConfig);

      // act
      await authService.load();

      // assert
      expect(userManagerSpy).toHaveBeenCalled();
    });

I'm getting a 404 error when running tests and I'm guessing the new UserManager(config) and/or the this.userManager.getUser() is trying to make an httpRequest when I want it to return the mock values.

How do I spyOn userManager and mock the return value from getUser()?

My understanding was that the TestBed.configureTestModule providers is for setting up the services which are DI'd into the service, not members of the service itself.

1

There are 1 best solutions below

0
Jakub Biernaczyk On

You need to spy on a constructor. In JS world you can do something like this

spyOn(window, 'UserManager').andReturn(...);

Topic about spying on constructor: Spying on a constructor using Jasmine

Instead of ... You can create a spy/mock and later check if #getUser() function was called. Or you can simply create Moco implementation for #getUser().

However remember that "Everytime a mock returns a mock a fairy dies". In other words - this is not the best way to write test and it indicates problem with code quality. If the UserManager was created inside function - it's not the service and I think it should not have logic.

Maybe you can refactor the code in a way that the config is used to call method of a service injected in constructor of AuthService?