我正在使用一个表单控件,它使用 valueChanges
和 debounceTime
检测变化。我正在编写一个监视 itemService
的测试,以检查是否正在调用 update
方法。如果我从表单控件中删除 debounceTime
,则测试工作正常。
这是组件中的表单控件。
this.itemControl.valueChanges.debounceTime(300).subscribe(response => {
this.itemService.update(response);
});
这是测试
it('should do stuff',
inject([ItemService], (itemService) => {
return new Promise((res, rej) =>{
spyOn(itemService, 'update');
let item = {
test: 'test'
};
fixture.whenStable().then(() => {
let itemControl = new FormControl('test');
fixture.componentInstance.itemControl = itemControl;
fixture.autoDetectChanges();
fixture.componentInstance.saveItem(item);
expect(itemService.update).toHaveBeenCalled();
})}));
这是组件的 saveItem 函数
saveItem(item): void {
this.itemControl.setValue(item);
}
就像我说的,如果我从表单控件中删除 debounceTime
测试执行正常,但我不能那样做。我尝试在 expect
调用之前添加一个 tick()
调用,但我只是收到此错误
Unhandled Promise rejection: The code should be running in the fakeAsync zone to call this function ; Zone: ProxyZone ; Task: Promise.then ; Value: Error: The code should be running in the fakeAsync zone to call this function Error: The code should be running in the fakeAsync zone to call this function
最佳答案
您应该使用 fakeAsync() 和 tick()。根据您的相关测试代码,查看在我这边成功运行的代码(.spec.ts 文件)。
下面代码解释:
fakeAsync()
和 tick()
应该始终一起使用。您可以一起使用 async()/fixtureInstance.whenStable()
,但从程序员的 Angular 来看,它不太“可预测”。我建议您尽可能使用 fakeAsync()/tick()
。你应该only当您的测试代码进行 XHR 调用(又名测试 Http 请求)时使用 async()/fixtureInstance.whenStable()
。
最好尽可能使用 fakeAsync()/tick()
,因为您可以手动控制异步代码在测试代码中的运行方式。
正如您在下面的代码(.spec.ts 文件)中看到的那样。你用方法参数300
调用tick方法很重要,tick(300)
,因为你设置的debounce值是300
。如果您假设将 debounce 值设置为 500
,那么您的测试代码中的 tick 值应该是 500
,如果您希望它在这种情况下通过。
您会注意到,如果您设置 tick(299)
,您的测试将失败,但这是正确的,因为您将 debounce 值设置为 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.`);
}
}
关于javascript - Angular2 - 使用 debounceTime 测试调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41641995/