Search code examples
angulartypescriptunit-testingts-jestangular-spectator

Angular jest spectator mock service dependency while testing another service


I am using,

Angular CLI: 10.2.3

Node: 12.22.1

The project builds and runs fine. I am trying to add tests now using Jest and Spectator. I have the following service; I am trying to run a very basic test (can mock most of the values).

@Injectable({
    providedIn: 'root'
})
export class BasicAuthService {
  environmentName = '';
  environmentUrl = '';

  constructor(
    private http: HttpClient,
    private config: ConfigService, //custom service 1
    private runtimeConfig: RuntimeConfigService, // custom service 2
  ) { 
      this.environmentName = runtimeConfig.config.environmentName;
      this.environmentUrl = this.environmentName == "localhost" 
          ? "http://" +  runtimeConfig.config.serviceUrl 
          : runtimeConfig.config.serviceUrl;    
  }
  
  getAuthentication(credentials) {
    let basicAuthHeaderString = 'Basic ' 
      + window.btoa(credentials.username + ':' + credentials.password);
    let headers = new HttpHeaders({'Content-Type': 'application/json'});
    let options = {
      headers: headers
    }
    let envUrl = `${this.environmentUrl}/api/login`
    return this.http.post<any>(envUrl, JSON.stringify(credentials), options)
      .pipe(
        map(
          data => {
          sessionStorage.setItem('authenticatedUser', credentials.username);
          sessionStorage.setItem('token', data.token);
          
          this.config.userGroupData = data.entitlements[0];

          }
        )
      );
  }


}

In the constructor, it is trying to set 2 variables (this.environmentName and this.environmentUrl) based on another custom service (runtimeConfig).

I am trying to test using the below:

describe('BasicAuthService', () => {
  let spectator: SpectatorService<BasicAuthService>;
  const createService = createServiceFactory({
    service: BasicAuthService,
    providers: [],
    imports: [
        HttpClientTestingModule],
    entryComponents: [],
    mocks: [ConfigService, RuntimeConfigService]
  });


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

  it('should be logged in', () => {
    
    const runtimeConfigService = spectator.inject<RuntimeConfigService>(RuntimeConfigService);
    const configService = spectator.inject<ConfigService>(ConfigService);
    runtimeConfigService.config = { 
      environmentName: "localhost", 
      serviceUrl : "localhost:8071"
    };     // This also does not work, same error.   
    expect(spectator.service.getAuthentication(createService)).toBeTruthy();
  });

});

This is failing with error:

  ? BasicAuthService > should be logged in

  TypeError: Cannot read property 'environmentName' of undefined

      22 |     private runtimeConfig: RuntimeConfigService,
      23 |   ) {
    > 24 |     this.environmentName = runtimeConfig.config.environmentName;
         |                                                 ^

The run time config is as below. I tried initializing the values but does not help.

// RuntimeConfigService
@Injectable({
  providedIn: 'root'
})
export class RuntimeConfigService {

  config: Config;
  
  constructor(private http: HttpClient) {}

  loadConfig() {
  return this.http
    .get<Config>('./assets/runtime-config.json')
    .toPromise()
    .then(config => {
        this.config = config;        
    });
  }
}

export class Config {
  serviceUrl: string;
  environmentName: string;
}

How do I mock these services and their values so I can get this basic test working?


Solution

  • I would try it with mockProvider function from spectator. You can easily mock the service with default values this way. It comes handy in the case when a code in the constructor depends on some value from the DI.

    import { mockProvider, ... } from '@ngneat/spectator/jest'; 
    
    describe('BasicAuthService', () => {
      let spectator: SpectatorService<BasicAuthService>;
      const createService = createServiceFactory({
        service: BasicAuthService,
        providers: [
          mockProvider(ConfigService, {
            someFunction: () => someReturnValue; //if needed
          }),
          mockProvider(RuntimeConfigService, {
            config: {
              environmentName: "localhost", 
              serviceUrl : "localhost:8071"
            }
          })
        ],
        imports: [HttpClientTestingModule],
        entryComponents: [],
      });
    
      beforeEach(() => spectator = createService());
    
      it('should be logged in', () => {
        expect(spectator.service.getAuthentication(createService)).toBeTruthy();
      });
    });