如何在 angular 测试中等待 xhr 请求

How to wait for an xhr request in an angular test

我有一个带按钮的组件。单击按钮时,会发出 HTTP 请求:

this.tokensService.create(this.userInCreation).subscribe(nextCb, errorCb);

如何等待这个请求完成?使用 async 实用程序和 fixture.whenStable 对我没有帮助。

例如:

it('', async( async () => {
    fixture.detectChanges();

    appPage.loginButtonEl.click(); // XHR request is initiated here
    fixture.detectChanges();

    await fixture.whenStable();

    // HTTP request is still pending here
}))

编辑:糟糕,我误会了什么。但我仍然认为您可以通过监视发生点击的组件来解决它,然后在测试中订阅服务功能,请参阅 stackblitz

对于您的测试,这意味着以下内容:

it('', (done) => {
  fixture.detectChanges();    

  // set up spy for the function that gets called in your click() function
  // and basically replacing it with your own implementation here so you can
  // subscribe to it and wait for completion
  let spy = spyOn(appPage, 'myFunction').and.callFake(() => {
  tokensService.create(appPage.userInCreation)
    .subscribe(data => {
      fixture.detectChanges();

      // more expect statements or whatever you need here

      done();
    });
  });

  appPage.loginButtonEl.click();
});

您可以使用 fakeAsync 而不是 async()。我创建了一个组件,一个 HTTP_INTERCEPTOR 到 return 1000 毫秒后的响应。这是代码

noop-interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse
} from '@angular/common/http';

import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';

/** Pass untouched request through to the next request handler. */
@Injectable()
export class NoopInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
      let response = new HttpResponse();
      response = response.clone({body: 'body'});
    return of(response).pipe(delay(1000));
  }
}

app.component.ts

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
  data;
  constructor(private http: HttpClient){}


  getData(){
    this.http.post('asd',{}).subscribe(data => {
      console.log(data);
      this.data = data;
    });
  }
}

app.component.html

<input type='button' (click)='getData()' value='Get Data' />

<div class='response'>{{data}}</div>

app.component.spec.ts

import { TestBed, async, ComponentFixture, tick, fakeAsync } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NoopInterceptor } from './noop-interceptor';
import { By } from '@angular/platform-browser';
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      imports: [
        
        HttpClientModule
      ],
      providers: [{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true }],
    }).compileComponents();
  }));
  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));
  it('should return response',fakeAsync(()=>{
    const fixture = TestBed.createComponent(AppComponent);
    const component = fixture.debugElement.componentInstance;
    fixture.detectChanges();
    component.getData();
    tick(1000);
    fixture.detectChanges();
    expect(component.data).toBe('body');
  
  }));
  
  it('should display response',fakeAsync(()=>{
    const fixture = TestBed.createComponent(AppComponent);
    const component = fixture.debugElement.componentInstance;
    fixture.detectChanges();
    component.getData();
    tick(1000);
    fixture.detectChanges();
    let element = fixture.debugElement.query(By.css('.response')).nativeElement;
    
    expect(element.textContent).toBe('body');
  
  }));
  
});

注意tick(1000),这将提前虚拟时钟

https://angular.io/guide/testing#the-tick-function

你需要import 'zone.js/dist/zone-testing';。如果项目是使用 angular-cli 创建的,那么它已经存在于 test.ts

阅读此处的指南以了解有关测试异步服务的更多信息:

https://angular.io/guide/testing#component-with-async-service

fakeAsync 包装你的测试并在你的测试中添加 tick()

it('', fakeAsync( async () => {
  fixture.detectChanges();

  appPage.loginButtonEl.click(); // XHR request is initiated here

  tick();

  fixture.detectChanges();

  await fixture.whenStable();

  // HTTP request is still pending here
}))

参考:
https://angular.io/api/core/testing/tick
https://angular.io/api/core/testing/fakeAsync