0

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.

B.Balamanigandan
  • 4,713
  • 11
  • 68
  • 130

1 Answers1

1

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/

terahertz
  • 2,915
  • 1
  • 21
  • 33
  • Thanks for your valuable information, It will take time to be online to check the code. I given a up-vote and once I get the result I'll approve it as answer. – B.Balamanigandan Jul 11 '19 at 04:50
  • Hi, Once I triggered the `flush` method, the chrome browser gets disconnect. Kindly assist me in `HEAD` request – B.Balamanigandan Jul 12 '19 at 06:10
  • My bad, I forgot it is a HEAD request. I have edited my answer for the "mock backend" to return headers and an empty http body as a HEAD request should do. The subscription should test if the headers returned are as expected. For the specifications, have a look at https://angular.io/api/common/http/testing/TestRequest#flush – terahertz Jul 12 '19 at 06:42
  • Still I'm facing issues. I posted the details in another question https://stackoverflow.com/questions/57001238/angular-jasmine-test-case-gets-failed-in-http-head-request-while-on-flush – B.Balamanigandan Jul 12 '19 at 06:45
  • I have read your new question, your service method is still incorrect though. Perhaps I didn't explain well enough, I have further edited my answer. – terahertz Jul 12 '19 at 06:53
  • The test case executed successfully `Chrome 75.0.3770 (Windows 10.0.0): Executed 1 of 92 SUCCESS (0 secs / 0.037 secs)` but chrome get crashed `12 07 2019 12:47:15.886:ERROR [launcher]: Chrome crashed - [14424:21784:0712/124715.833:ERROR:browser_process_sub_thread.cc(221)] Waited 275 ms for network service` – B.Balamanigandan Jul 12 '19 at 07:13