Angular2 - 使用 debounceTime 测试调用

Posted

技术标签:

【中文标题】Angular2 - 使用 debounceTime 测试调用【英文标题】:Angular2 - Testing call with a debounceTime 【发布时间】:2017-05-29 06:16:22 【问题描述】:

我正在使用一个表单控件,该控件使用valueChangesdebounceTime 检测更改。我正在编写一个监视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

【问题讨论】:

我认为您需要将测试包装在async 中才能使用tick。谴责的一种选择是注入去抖时间,因此您可以将其设置为较小的数字以保持测试速度。 不管去抖时间有多长,可以是 0 或 1。测试失败,Expected spy update to have been called. 【参考方案1】:

您应该使用 fakeAsync()tick()。查看下面的代码(.spec.ts 文件),根据您有问题的测试代码在我端成功运行。

以下代码说明:fakeAsync()tick() 应始终一起使用。您可以一起使用async()/fixtureInstance.whenStable(),但从程序员的角度来看,它不太“可预测”。我建议您尽可能使用fakeAsync()/tick()。当您的测试代码进行 XHR 调用(也就是测试 Http 请求)时,您应该 only 使用 async()/fixtureInstance.whenStable()

最好尽可能使用fakeAsync()/tick(),因为您可以手动控制异步代码在测试代码中的运行方式。

您可以在下面的代码(.spec.ts 文件)中看到。使用方法参数300tick(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.`);
  

【讨论】:

即时结果..!!

以上是关于Angular2 - 使用 debounceTime 测试调用的主要内容,如果未能解决你的问题,请参考以下文章

js工具函数,自己封装一个节流函数

js工具函数,自己封装一个节流函数

TSLint 烦人的消息

404 使用 Angular2、angular2-websocket 和类型

找不到模块'angular2/angular2'

在 Angular2 应用程序中使用 angular2-websocket 关闭 websocket 连接时,如何重新连接它?