Test case for a service method which contains a HTTP subscribe - Angular HTTP RxJs

1.4k views Asked by At

I'm having a service method and it has a service call (HTTP call) and it subscribes immediately and based on the response code it executes the rest of the action command.

Example: Service Method

processData(id): void {
    const url = `http://localhost:5000/data/${id}`;

    this.http.head(url).subscribe(() => {
        console.log('Success');

        // Rest of the code - TODO

    }, (error) => {
        console.log('Failed');        

        // Rest of the code - TODO

    });
}

I tried the following sample (Test case)

fdescribe('ReportedFileService', () => {

    let service: DataService;
    let httpMock: HttpTestingController;

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports:[HttpClientModule, HttpClientTestingModule],
            providers:[DataService]
        });
        service = TestBed.get(DataService);
        httpMock = TestBed.get(HttpTestingController);
    });

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

    fit('should be valid', () => {
        const id = 1;
        const filePath = `http://localhost:5000/data/${id}`;

        const req = httpMock.expectOne(filePath);

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

        const response = service.processData(id);
    })
}

kindly assist me how to handle this situation.

1

There are 1 answers

6
terahertz On

Your service shouldn't be subscribing to the HttpClient observable and thus, it shouldn't be a void return type method. Services should be returning a HttpClient observable to be subscribed to.

E.g.

Service Method

@Injectable({ providedIn: 'root' }) //ensure service is provided at root level so it remains a singleton in the dependency injection tree.
...
constructor(http: HttpClient){}
...
processData(id): Observable<any> { //services should return an Observable
    const url = `http://localhost:5000/data/${id}`;
    return this.http.head(url); // ** your service method SHOULDN'T be subscribing to the HTTP call.
}

Your service method shouldn't be subscribing to the HTTP call. Invoking .subscribe() will cause the HTTP request to be made.

Component using this service will first inject the service in the constructor. Then, you will subscribe to the service call in the component.

SomeComponent.ts

...
constructor(private dataService: DataService){}
...
someMethod(){
   this.processData().subscribe(
       (response) => { //subs
           console.log("success");
           // Rest of the code - TODO
       },
       (error) => {
           console.log('Failed');        

          // Rest of the code - TODO
       }
   )
}

Your test case should then subscribe to the service just like a component.

service.spec.ts - your service test case

fit('should be valid', fakeAsync(() => {
   const id = 1;

   service.subscribe( //you are making the http call in the test case.
       (success: any) => {
          expect(success.request.headers.get('Content-Type').toEqual('application/json')); //assert that the http headers you will get back from the observable is similar to the one the mock backend will return.
       }
   )

   httpMock.expectOne({
      url: 'http://localhost:5000/data/${id}',
      method: 'HEAD'
   }).flush({}, { headers: { 'Content-Type': 'application/json' } }); //HEAD requests have empty response body but only headers

});

Also, you shouldn't be calling localhost, when you have to deploy this app into a web server, you'd then have to manually change each string.

Instead you should be setting your API urls in the environments file, which is located in:

You'd then import the environment urls as strings in such a manner:

import { environment } from 'environments/environment';
...
const API_URL = environment.apiURL;

Here are some guides which had helped me and I had bookmarked: Using angular's HttpClient module to send HTTP requests: https://www.techiediaries.com/angular-httpclient/

Testing services: https://www.ng-conf.org/2019/angulars-httpclient-testing-depth/