您应该使用 fakeAsync() 和 tick()。查看下面的代码(.spec.ts 文件),根据您有问题的测试代码在我端成功运行。
以下代码说明:
fakeAsync() 和 tick() 应始终一起使用。您可以一起使用async()/fixtureInstance.whenStable(),但从程序员的角度来看,它不太“可预测”。我建议您尽可能使用fakeAsync()/tick()。当您的测试代码进行 XHR 调用(也就是测试 Http 请求)时,您应该 only 使用 async()/fixtureInstance.whenStable()。
最好尽可能使用fakeAsync()/tick(),因为您可以手动控制异步代码在测试代码中的运行方式。
您可以在下面的代码(.spec.ts 文件)中看到。使用方法参数300、tick(300)调用tick方法对你来说非常重要,因为你设置的去抖值是300。如果您假设将 debounce 值设置为500,那么如果您希望它在这种情况下通过,那么您的测试代码中的刻度值应该是500。
您会注意到,如果您设置tick(299),您的测试将失败,但这是正确的,因为您将去抖动值设置为300。这向您展示了使用fakeAsync()/tick() 的力量,您可以控制代码时间(当您使用fakeAsync()/tick() 时,您就是时间的主人)。
// component.sandbox.spec.ts
import { async, TestBed, fakeAsync, tick, inject } from "@angular/core/testing";
import { ReactiveFormsModule } from "@angular/forms";
import { SandboxComponent } from "./component.sandbox";
import { ItemService } from "../../Providers";
import "rxjs/add/operator/debounceTime";
describe("testFormControl", () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ReactiveFormsModule],
declarations: [SandboxComponent],
providers: [ItemService],
}).compileComponents();
}));
// The test you had questions about :)
it("(fakeAsync usage) Should hit the ItemService instance's 'update' method once", fakeAsync(inject([ItemService], (itemService: ItemService) => {
spyOn(itemService, "update");
let fixture = TestBed.createComponent(SandboxComponent);
fixture.detectChanges(); // It is best practices to call this after creating the component b/c we want to have a baseline rendered component (with ng2 change detection triggered) after we create the component and trigger all of its lifecycle events of which may cause the need for change detection to occur, in the case attempted template data bounding occurs.
let componentUnderTest = fixture.componentInstance;
componentUnderTest.saveItem("someValueIWantToSaveHEHEHE");
tick(300); // avoliva :)
expect(itemService.update).toHaveBeenCalled();
})));
});
// component.sandbox.ts
import { Component, OnInit } from "@angular/core";
import { FormGroup, FormControl } from "@angular/forms";
import { ItemService } from "../../Providers";
@Component({
template: `
<form [formGroup]="formGroupInstance">
<input formControlName="testFormControl" />
<button type="submit">Submit</button>
<button type="button" (click)="saveItem(formGroupInstance.controls['testFormControl'].value)">saveItem(...)</button>
</form>
`,
styleUrls: ["component.sandbox.scss"],
})
export class SandboxComponent extends OnInit {
public formGroupInstance: FormGroup;
public testFormControlInstance: FormControl;
constructor(private itemService: ItemService) {
super();
this.testFormControlInstance = new FormControl();
this.formGroupInstance = new FormGroup(
{
testFormControl: this.testFormControlInstance,
},
);
}
public ngOnInit() {
this.testFormControlInstance.valueChanges
.debounceTime(300) // avoliva
.subscribe((formControlInstanceValue: {}) => {
this.itemService.update(formControlInstanceValue);
});
}
public saveItem(item: any) {
this.testFormControlInstance.setValue(item);
}
}
// ../../Provider/index.ts
export class ItemService {
public update(formControlInstanceValue: any) {
// Makes http request to api to update item
console.log(`HEY PROGRAMMER, YEAH YOU! :P \n => http request could have been made
here to update an 'item' in the database.`);
}
}