测试 HTTP 请求的请求和结果之间的条件

Test a condition between the request and result of a HTTP request

假设一个组件方法创建一个 HTTP GET 请求,在等待响应时设置加载状态,并在给出响应时删除加载状态。例如:

getData() {
    this.myService
      .getSomeDate()
      .pipe(tap(_ => {
        this.loading = true;
      }))
      .subscribe(result => {
        this.data = result;
        this.loading = false;
      });
}

您将如何通过测试验证请求和响应之间的状态(例如 pipe 部分)确实正在加载?我尝试使用 HttpTestingController 发送 HttpEventType.Sent 事件,但似乎不起作用。调用 req.flush() 将导致代码立即转到 subscribe 部分,因此这也不起作用。这是我用过的测试方法:

it('should have a loading state durin the request', () => {
    component.getData();
    const req = httpClient.expectOne('http://localhost:8080');
    req.event({type: HttpEventType.Sent});

    expect(component.loading).toBe(true);

    req.flush();
    expect(component.loading).toBe(false);
});

备注

  1. 请注意 Observable.pipe 仅在结果值到达时调用。因此,在 getData 方法中,在调用 myService.getSomeDateservice 之前应将 loading 状态设置为 true 并且不需要 pipe

  2. component进行单元测试时,应该mock相关的服务方法。

组件

getData() {
    this.loading = true;
    this.myService.getSomeDate()
      .subscribe(result => {
        this.data = result;
        this.loading = false;
      });
}

单元测试

现在您可以尝试 运行 在伪异步区域中进行测试(使用 fakeAsync)并在两者之间使用 flush exec.

import { ComponentFixture, TestBed, fakeAsync, flush} from '@angular/core/testing';
import { Observable, of } from 'rxjs';

...

it('should have a loading state durin the request', fakeAsync(() => {
    const myService = TestBed.get(MyService);
    const data = ... // define data to be returned by myService.getSomeDate
    spyOn(myService, 'getSomeDate').and.returnValue(of(data));

    component.getData();

    expect(component.loading).toBe(true);    
    flush();
    expect(component.loading).toBe(false);
}));

更新

of returns 同步传递作为参数提供的值的 Observable。因此,上面的测试实际上并没有涉及异步代码。正如下面评论中 Andrei Gătej 所提到的,这使得 flush 变得毫无用处。通过pipe(delay(1)),我们可以模拟异步处理,现在将使用tick来模拟时间的异步流逝。

it('should have a loading state durin the request', fakeAsync(() => {    
    ....
    spyOn(myService, 'getSomeDate').and.returnValue(of(data).pipe(delay(1)));

    component.getData();

    expect(component.loading).toBe(true);    
    tick(1);
    expect(component.loading).toBe(false);
}));