Repository: angular
Issue: Cannot test JSONP HTTP request using the HttpClientTestingModule and HttpTestingController
Labels: type: bug/fix,freq1: low,area: common/http,state: confirmed,design complexity: low-hanging,P4

<!--
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.

ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
-->

## I'm submitting a...
<!-- Check one of the following options with "x" -->
<pre><code>
[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  <!-- Please search GitHub for a similar issue or PR before submitting -->
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
</code></pre>

## Current behavior
<!-- Describe how the issue manifests. -->
When I try to mock a JSONP HTTP request using the HttpClientTestingModule, I get the error

`Error: Expected one matching request for criteria "Match URL: https://archive.org/index.php?output=json&callback=callback", found none.`

I posted a [stackoverflow question](https://stackoverflow.com/questions/47703877/how-do-i-test-a-jsonp-http-request-in-angular) about this issue.

## Expected behavior
<!-- Describe what the desired behavior would be. -->
A TestRequest to be returned from `httpMock.expectOne(service.url);` in the following code sample.

## Minimal reproduction of the problem with instructions
<!--
For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5).
-->
I'm using the [HttpClientModule][1] and [HttpClientJsonpModule][2] to make a JSONP HTTP request in a service.

**app.module.ts**

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';
    
    import { AppComponent } from './app.component';
    
    @NgModule({
      declarations: [
        AppComponent
      ],
      imports: [
        BrowserModule,
        HttpClientModule,
        HttpClientJsonpModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }

This service uses the [jsonp][3] method from the [HttpClient class][4] to get a JSONP response from the specified URL. 

**example.service.ts**

    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    
    @Injectable()
    export class ExampleService {
      url = "https://archive.org/index.php?output=json&callback=callback";
    
      constructor(private http: HttpClient) { }
    
      getData() {
        return this.http.jsonp(this.url, 'callback');
      }
    }

Using the [HttpClientTestingModule][7], I inject the [HttpTestingController][8] so I can mock and flush my JSONP HTTP request.

**example.service.spec.ts**

    import { TestBed, inject } from '@angular/core/testing';
    import { 
      HttpClientTestingModule,
      HttpTestingController
    } from '@angular/common/http/testing';
    
    import { ExampleService } from './example.service';
    
    describe('ExampleService', () => {
      let service: ExampleService;
      let httpMock: HttpTestingController;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          imports: [HttpClientTestingModule],
          providers: [ExampleService]
        });
    
        service = TestBed.get(ExampleService);
        httpMock = TestBed.get(HttpTestingController);
      });
    
      describe('#getData', () => {
        it('should return an Observable<any>', () => {
          const dummyData = { id: 1 };
    
          service.getData().subscribe(data => {
            expect(data).toEqual(dummyData);
          });
    
          const req = httpMock.expectOne(service.url); // Error
          expect(req.request.method).toBe('JSONP');
          req.flush(dummyData);
        });
      });
    });

If I change the request method to GET, this test works as expected.

Also, on Gitter @alxhub showed me that I could get the expected return value from `httpMock.expectOne(service.url);` if I change `example.service.spec.ts` to the following:

    import { TestBed, inject } from '@angular/core/testing';
    import {
      HttpClientJsonpModule,
      HttpBackend,
      JsonpClientBackend
    } from '@angular/common/http';
    import { 
      HttpClientTestingModule,
      HttpTestingController
    } from '@angular/common/http/testing';
    
    import { ExampleService } from './example.service';
    
    describe('ExampleService', () => {
      let service: ExampleService;
      let httpMock: HttpTestingController;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          imports: [HttpClientTestingModule],
          providers: [ExampleService, { provide: JsonpClientBackend, useExisting: HttpBackend }]
        });
    
        service = TestBed.get(ExampleService);
        httpMock = TestBed.get(HttpTestingController);
      });
    
      describe('#getData', () => {
        it('should return an Observable<any>', () => {
          const dummyData = { id: 1 };
    
          service.getData().subscribe(data => {
            expect(data).toEqual(dummyData);
          });
    
          const req = httpMock.expectOne(request => request.url === service.url);
          expect(req.request.method).toBe('JSONP');
          req.flush(dummyData);
        });
      });
    });

  [1]: https://angular.io/api/common/http/HttpClientModule
  [2]: https://angular.io/api/common/http/HttpClientJsonpModule
  [3]: https://angular.io/api/common/http/HttpClient#jsonp
  [4]: https://angular.io/api/common/http/HttpClient
  [5]: https://angular.io/api/common/http/JsonpInterceptor
  [6]: https://angular.io/api/common/http/JsonpClientBackend
  [7]: https://angular.io/api/common/http/testing/HttpClientTestingModule
  [8]: https://angular.io/api/common/http/testing/HttpTestingController

## What is the motivation / use case for changing the behavior?
<!-- Describe the motivation or the concrete use case. -->
To be able to test JSONP HTTP requests using the HttpClientTestingModule and HttpTestingController.

## Environment

<pre><code>
Angular version: 5.0.5
<!-- Check whether this is still an issue in the most recent Angular version -->

Browser:
- [x] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: 8.9.1  <!-- run `node --version` -->
- Platform:  Mac OS 10.13.1 <!-- Mac, Linux, Windows -->

Others:
<!-- Anything else relevant?  Operating system version, IDE, package manager, HTTP server, ... -->
</code></pre>