使用 Jasmine 和 await 测试基于 HTTP 的异步函数:预期有一个匹配条件“...”的请求,找到 none

Testing async HTTP-based function with Jasmine and await: Expected one matching request for criteria "...", found none

内容:测试使用 await

的异步函数

搭配:Angular9,茉莉花 3.4.0

要重现的最少代码:StackBlitz

我有这样的功能。请注意,它必须等待 this.getHeaders()。每当我删除 await 并用一些同步实现替换 getHeaders() 的实现时,测试都会成功运行。

正确的测试方法是什么?

private async loadUserData() {
    // Try to remove await - the test runs successfully
    //const headers = this.getHeaders();
    const headers = await this.getHeaders();
    return this.httpClient
      .get<UserData>(window.location.origin + '/12345', { headers })
      .toPromise()
      .then((data) => {
        this.userData = data;
      })
      .catch((e) => {
        console.log('Oh noooo...');
      });
  }

我尝试了什么:

不是被骗的:

你非常接近。您的测试现在结构正确,但您需要 Angular 提供的一些测试实用程序来确保您的 Promise 以正确的顺序解析,以使您的期望正确。

最终问题与 Angular 区域有关。因为您在您的服务中构建承诺,并且承诺解决必须在传出请求发生之前发生。您在测试中调用 loadUserData,然后在下一行中编写一个断言 "make sure that this request happened, if not that's an error"。当您以不使用异步原语(如 PromiseObservable)的方式编写 header 检索函数时,会发生此请求 "immediately"。但是当你使用 Promise 时,没有请求发生 "immediately"。相反,它必须先解析您的 header 检索函数。

幸运的是,您的测试失败只是测试环境的幻象功能,而不是您实现中的错误。正如我所说,Angular 为您提供了一些测试工具,以确保您可以在编写断言之前 "flush" 所有未决的承诺。

import { fakeAsync,  tick } from '@angular/core/testing';

// instead of using `done`, which is just fine btw, we wrap our test function
// in fakeAsync(). This let's us have fine grained control over stepping through
// our code under test
it('should return user data on success', fakeAsync(() => {
  const mockUserData : UserData = {
    name: 'John',
      surname: 'Do',
      id: '12345'
    };

    (service as any).loadUserData().then(() => {
      expect((service as any).userData).toEqual(mockUserData);
      //expect(req.request.method).toEqual('GET');
    });

    // tick() steps through the next round of pending asynchronous activity
    // this will also step through 'setTimeout' and 'setInterval' code
    // you may have in your service, as well as Observable code
    tick();
    const req = httpTestingController.expectOne(
      'https://testing-qh48mg.stackblitz.io/12345',
    );
    req.flush(mockUserData);
}));

已更新Stackblitz. Docs on fakeAsync. Related question on testing Http code in Angular using tick.