3

I am trying to unit test this function in my service that first executes POST request and then executes a GET afterwards. I'm using switchMap to accomplish this but the problem I'm having is that both of the requests are not getting picked up the the HttpTestingController match function

Here is the service function I want to test:

save(cow: Cow): Observable<Object> {
  return this.http.put<Cow>(`${this.cowUrl}/1`, cow, this.httpOptions)
    .pipe(switchMap(_ => {
       return this.getAllCows();
    })
  );
}

private getAllCows(): Observable<Cow[]> {
  return this.http.get<Cow[]>(`${this.cowUrl}`).pipe(tap(data => {
      this.cows = data;
    }),
    catchError(this.handleError<Cow[]>('getAllCows'))
  );
}

Here is the spec for that function:

describe('CowService', () => {
  let cowService: CowService;
  let httpMock: HttpTestingController;

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

    cowService = TestBed.get(CowService);
    httpMock = TestBed.get(HttpTestingController);
  });

  afterEach(() => {
    httpMock.verify();
  });

  describe('save', () => {
    const cowList: Cow[] = [
      { id: '1', name: 'Cow' },
      { id: '2', name: 'Another Cow' }
    ];
    it('Successfully saves cow and updates the list of cows', () => {
      const cow: Cow = { id: null, name: 'Third Cow' };

      cowService.save(cow).subscribe();

      const reqs = httpMock.match(request =>  request.url = '/api/cows');

      console.log(reqs); // shows only the POST request and not the GET

      expect(reqs[0].request.url).toEqual('/api/cows');
      expect(reqs[0].request.method).toEqual('POST');

      expect(reqs[1].request.url).toEqual('/api/cows');
      expect(reqs[1].request.method).toEqual('GET');

      reqs[0].flush({});
      reqs[1].flush(cowList);
    });
  });
});

When doing this I get an error that says:

TypeError: Cannot read property 'flush' of undefined

Clev_James23
  • 171
  • 1
  • 3
  • 15
  • Does `addCow` call `save`? – AliF50 Mar 24 '20 at 13:01
  • My apologies, I updated the question to reflect the correct function names – Clev_James23 Mar 24 '20 at 13:26
  • Your flush sequencing seems wrong. To jump from one call to another, you make your call, do your expect/match, flush, then repeat the process for the next call, eventually calling verify, which ensures you have no dangling http calls outstanding (so you know you completed your sequence). – Tim Consolazio Mar 24 '20 at 13:57
  • I definietly did not consider the order of operations in the situation. Got it working per your and @AliF50 suggestions – Clev_James23 Mar 24 '20 at 14:41

1 Answers1

3

Try the following:

    it('Successfully saves cow and updates the list of cows', () => {
      const cow: Cow = { id: null, name: 'Third Cow' };

      cowService.save(cow).subscribe(
        response => expect(response).toEqual(cowList);
      );

      const putCall = httpMock.expectOne('/api/cows');
      expect(putCall.request.method).toEqual('PUT');
      // flush what the put call should return
      putCall.flush({});

      const getCall = httpMock.expectOne('/api/cows');
      expect(getCall.request.method).toEqual('GET');
      // flush what the get call should return
      getCall.flush(cowList);
    });
AliF50
  • 16,947
  • 1
  • 21
  • 37
  • 1
    That worked! I tried a variation of that earlier but definitely didn't consider the order of the expects and flush. Thanks!! – Clev_James23 Mar 24 '20 at 14:39
  • 1
    This only works if the methods of the sequential requests are different, is that correct? – MartaGalve Jan 07 '21 at 07:45
  • That's correct but I think you can use the `match` helper to match all requests. https://angular.io/api/common/http/testing/HttpTestingController – AliF50 Jan 07 '21 at 15:24