Stephen Whitmore
Stephen Whitmore

Reputation: 953

Angular Unit Test: How to cover activity inside service stub method subscriptions

I'm writing unit tests for one of my Angular component methods which assigns a property the response value from a service call and calls another method.

I have my service stubbed with response data and I subscribe to it inside my test with the expect statements inside the subscription but it keeps showing the value of the property as being an empty array. I've confirmed the "response" in the test below contains the mock data but cannot get the component property "resultSet" to show as assigned a value. The spy for the "toggleSearchForm()" method never appears to be called either.

The method being tested: search.component.ts

submitSearchCriteria() {
    this.searchService.searchRequest(this.myForm.value)
        .pipe(take(1))
        .subscribe(response => {
            this.resultSet = response;
            this.toggleSearchForm();
        });
}

The failing test: search.component.spec.ts

it('should assign resultSet to response data and trigger toggle', fakeAsync(() => {
    const spy = spyOn(component, 'toggleSearchForm');
    component.myForm.controls['field1'].setValue('some search query');
    component.myForm.controls['field2'].setValue('something that narrows it down more');

    searchServiceStub.searchRequest(component.myForm.value)
        .subscribe(response => {
            expect(component.resultSet).toContain(response);
            expect(spy).toHaveBeenCalled();
            expect(spy.calls.count()).toBe(1);
        });

    tick();
}))

The service stub: search-service.stub.ts

...
const searchResults = require('./test-data/search-results.json');

searchRequest(searchCriteria) {
    if (!searchCriteria) {
        return of([])
    }
    return of(searchResults);
}

I expect resultSet to contain the stub response and the spy to have been called but the test fails with the following error messages:

Expected [  ] to contain [ Object({ thingName: 'thing i was searching for', thingId: 1234 }) ].

and

Error: Expected spy toggleSearchForm to have been called.

Upvotes: 0

Views: 1567

Answers (1)

Wesley Trantham
Wesley Trantham

Reputation: 1264

I think your component test would be more meaningful if it tested the component method like so.

it('should assign resultSet to response data and trigger toggle', () => {
  const spy = spyOn(component, 'toggleSearchForm');
  const searchService = TestBed.get(SearchService) as SearchService;
  const serviceSpy = spyOn(searchService, 'searchRequest').and.callThrough();
  component.myForm.controls['field1'].setValue('some search query');
  component.myForm.controls['field2'].setValue('something that narrows it down more');

  component.submitSearchCriteria();

  expect(component.resultSet).toEqual([{ thingName: 'thing i was searching for', thingId: 1234 }]);
  expect(serviceSpy).toHaveBeenCalledWith(component.myForm.value);
  expect(spy.calls.count()).toBe(1);
  });

To make this work, your configuration should look something like

TestBed.configureTestingModule({
  declarations: [SearchComponent],
  providers: [{provide: SearchService, useClass: SearchServiceStub}],
  imports: [ReactiveFormsModule],
})

Important changes to note:

  • To check what the service was called with you can check the new serviceSpy, before you were testing that the static results matched your input
  • The search results we're going to compare what is coming from your search-results.json since that is what your search service is returning
  • No longer subscribing as that happens in your component.submitSearchCriteria, which we're now calling instead of your stub service

Upvotes: 2

Related Questions