Search code examples
angularunit-testingangular-httpclient

Angular HttpClient unit tests won't fail when comparing data inside of an asynchronous function


I'm trying to unit test a few simple GET requests and no matter what I do, I can't get the tests to fail.

If I change the ('GET') to ('POST') it will fail, but all of the api data passes no matter what.

import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';

import { mockPhoneNumbers } from '../mocks/data/phoneNumbers.mock';
import { PhoneNumberApiService } from './phone-number-api.service';

describe('PhoneNumberApiService', () => {
  let service: PhoneNumberApiService;
  let httpTestingController: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [HttpClientTestingModule],
      providers: [PhoneNumberApiService],
    });

    service = TestBed.get(PhoneNumberApiService);
    httpTestingController = TestBed.get(HttpTestingController);
  });

  afterEach(() => {
    // After every test, assert that there are no more pending requests.
    httpTestingController.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should get the phone numbers successfully', () => {
    service
      .getPhoneNumbers()
      .subscribe(phoneNumbers => {
        expect(phoneNumbers).toEqual('bob');
        expect(phoneNumbers[0].id).toEqual('b8bfea4d-a26f-9e4e-cbd4-39eb69cdaa58');
        expect(phoneNumbers[1].friendlyName).toEqual('Dev Test');
      });

    const req = httpTestingController.expectOne('phoneNumbers');

    expect(req.request.method).toEqual('GET');

    req.flush(mockPhoneNumbers);
  });

  it('should get the phone number details successfully', () => {
    const { id: phoneNumberId } = mockPhoneNumbers[0];

    service
      .getPhoneNumberDetails(phoneNumberId)
      .subscribe(phoneNumber => expect(phoneNumber).toEqual(mockPhoneNumbers[0]));

    const req = httpTestingController.expectOne(`phoneNumbers/${phoneNumberId}`);

    expect(req.request.method).toEqual('GET');

    req.flush('bob');
  });
});

Surely flushing the request with the mock data and then expecting the mock data to be bob is wrong. In the bottom test, flushing the request with bob and expecting the data to be equal to the first phone number in the array should fail.


Solution

  • The problem about your tests is that you make your "it" functions and expects asynchronous without explicit telling jasmine that .

    You need to use the done function in other to tell for the test waiting for something (check out in here a good tutorial about async test on jasmine)

    Follows an example based on your code:

    ...
    //Receive the done function like this
    it('should get the phone numbers successfully', (done) => {
        service
          .getPhoneNumbers()
          .subscribe(phoneNumbers => {
            expect(phoneNumbers).toEqual('bob');
            expect(phoneNumbers[0].id).toEqual('b8bfea4d-a26f-9e4e-cbd4-39eb69cdaa58');
            expect(phoneNumbers[1].friendlyName).toEqual('Dev Test');
            //Tell the test that only in here all the work was done
            done();
          });
        const req = httpTestingController.expectOne('phoneNumbers');
    
        expect(req.request.method).toEqual('GET');
    
        req.flush(mockPhoneNumbers);
    });
    ....
    

    Also, to answer your guess, jest is a test runner and is build on top of jasmine framework (meaning the jest syntax is similar to jasmin, but not equal). But for this case I guess that usage of the done will solve your problem.