【问题标题】:Test case for a service method which contains a HTTP subscribe - Angular HTTP RxJs包含 HTTP 订阅的服务方法的测试用例 - Angular HTTP RxJs
【发布时间】:2019-07-10 11:40:34
【问题描述】:

我有一个服务方法,它有一个服务调用(HTTP 调用),它会立即订阅并根据响应代码执行其余的操作命令。

示例:服务方法

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

    });
}

我尝试了以下示例(测试用例)

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);
    })
}

请帮助我如何处理这种情况。

【问题讨论】:

    标签: angular unit-testing karma-jasmine angular-test httptestingcontroller


    【解决方案1】:

    您的服务不应该订阅 HttpClient observable,因此,它不应该是一个 void 返回类型的方法。服务应该返回一个被订阅的 HttpClient observable。

    例如

    服务方式

    @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.
    }
    

    您的服务方法不应订阅 HTTP 调用。 调用 .subscribe() 将导致发出 HTTP 请求。

    使用该服务的组件将首先在构造函数中注入该服务。然后,您将订阅组件中的服务调用。

    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
           }
       )
    }
    

    然后您的测试用例应该像订阅组件一样订阅服务。

    service.spec.ts - 您的服务测试用例

    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
    
    });
    

    另外,您不应该调用 localhost,当您必须将此应用程序部署到 Web 服务器时,您必须手动更改每个字符串。

    相反,您应该在环境文件中设置您的 API url,该文件位于:

    然后,您可以通过以下方式将环境 url 作为字符串导入:

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

    这里有一些对我有帮助的指南,我已经收藏了: 使用 Angular 的 HttpClient 模块发送 HTTP 请求: https://www.techiediaries.com/angular-httpclient/

    测试服务: https://www.ng-conf.org/2019/angulars-httpclient-testing-depth/

    【讨论】:

    • 感谢您提供宝贵的信息,需要一些时间才能在线查看代码。我投了赞成票,一旦我得到结果,我就会批准它作为答案。
    • 嗨,一旦我触发了flush 方法,chrome 浏览器就会断开连接。请协助我HEAD请求
    • 我的错,我忘了这是一个 HEAD 请求。我已经编辑了“模拟后端”的答案,以返回标头和空的 http 正文作为 HEAD 请求应该执行的操作。订阅应测试返回的标头是否符合预期。有关规格,请查看angular.io/api/common/http/testing/TestRequest#flush
    • 我仍然面临问题。我在另一个问题stackoverflow.com/questions/57001238/… 中发布了详细信息
    • 我已经阅读了您的新问题,但您的服务方法仍然不正确。也许我解释得不够好,我已经进一步编辑了我的答案。
    猜你喜欢
    • 2018-01-17
    • 1970-01-01
    • 2018-12-11
    • 2017-11-21
    • 2022-06-10
    • 2012-08-18
    • 1970-01-01
    • 2018-12-11
    • 2019-10-27
    相关资源
    最近更新 更多