How to correctly use HttpClientTestingModule in Angular 9 with Jasmine/Karma

Viewed 5564

We have a generic service to make and receive calls to backend controllers. In Angular 4 all the tests were working, but due to new libraries and changes they no longer work. I am now trying to re-write these using HttpClientTestingModule and HttpTestingController.

So our concrete class has a number of functions and for the moment I'm concentrating on our getRequest method:

constructor(
    private configService: ConfigService,
    private userService: UserService,
    private httpClient: HttpClient,
    private errorMapperService: ErrorMapperService) {
  }

  public getRequest(uri: string): Observable<any> {

    const fullUri = this.buildServiceUri(uri);
    const requestHeaders = this.getDefaultRequestHeadersObservable();

    return forkJoin(fullUri, requestHeaders)
      .pipe(flatMap((results: [string, HttpHeaders]) => this.httpClient.get(results[0], { withCredentials: true, headers: results[1] })),
        catchError(this.errorMapperService.handleError));

  }

At the moment in my spec file I have:

fdescribe('Api Service', () => {

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientModule,
        HttpClientTestingModule
      ],
      providers: [
        { provide: ApiService },
        { provide: ConfigService },
        { provide: ErrorMapperService },
        { provide: UserService }
      ]
    });
  });

  describe('getRequest', () => {

    const uiServiceUri: string = 'uiServiceUri';
    const expectedResponse: any = { data: '1234', myDate: new Date(2017, 0, 1, 0, 0, 0) };
    const user = new User();
    user.userSession = '<SESSIONTOKEN>';

    let configService: ConfigService;
    let userService: UserService;
    let apiService: ApiService;
    let errorMapperService: ErrorMapperService;

    let httpTestingController: HttpTestingController;

    beforeEach(() => {
      configService = TestBed.get(ConfigService);
      userService = TestBed.get(UserService);
      apiService = TestBed.get(ApiService);
      errorMapperService = TestBed.get(ErrorMapperService);

      httpTestingController = TestBed.get(HttpTestingController);

    });

    describe('with response object',
      () => {

        it('Gets expected response and converts dates to date objects', async(() => {
          apiService.getRequest('test')
            .subscribe((responseObject) => {
              expect(responseObject).toEqual(expectedResponse);
            });

          httpTestingController.expectOne('test').flush(expectedResponse);

          httpTestingController.verify();
        }));
     });
    });

The issue is that if I initially leave out the last 2 lines of code against the httpTestingController, then my expect never gets reached and the test harness says 'no expectation found'. If I introduce those 2 lines back in, then I receive a fail that states:

Failed: Expected one matching request for criteria "Match URL: test", found none. Requests received are: GET http://localhost:9876/api/User/, GET http://localhost:9876/api/Config/UiServiceUri, GET http://localhost:9876/api/User/.

So then I change the URL to the main one such that the test controller line becomes:

httpTestingController.expectOne('http://localhost:9876/api/User/');

then I receive a similar error stating that only one was expected, but found 2. I'm guessing this is because of my constructor of the concrete class. How should I be writing this test?

0 Answers
Related