angular - 为什么在 Jasmine Angular 单元测试中没有触发响应式(Reactive)表单控件的更改事件?

标签 angular typescript unit-testing jasmine angular-reactive-forms

我有一个更改函数,我正在传递给一个响应式(Reactive)表单控件的更改事件,它评估状态并检查控件上是否有任何错误如果是这样,则将 bool 标志设置为真/假。然后使用此 bool 标志来确定是否显示 <div>。有错误消息的元素。这在浏览器中工作得很好,但是当单元测试运行时,“dirty”永远不会被设置为 true。这是我的代码:

HTML

<form [formGroup]="myForm" novalidate>
    <input id="age" formControlName="age" (change)="onChange()" />
    <div id="ageError" *ngIf="ageIsError()">
        <label>Age has errored</label>
    </div>
</form>

组件

constructor(private fb: FormBuilder) {}

ngOnInit() {
    this.myForm = this.fb.group({
        age: [null, [Validators.min(18)]]
    });
}

onChange() {
    if (this.ageIsError())
        // do something
}

ageIsError() {
    return this.myForm.controls.age.hasError('min') &&
           this.myForm.controls.age.dirty;
}

单元测试

it('should show error message when age is less than 18', fakeAsync(() => {
    let age = component.myForm.controls.age;
    age.setValue('10', { emitEvent: true });
    fixture.detectChanges();

    fixture.whenStable().then(() => {
        let ageError = debugElement.query(By.css('#ageError')).nativeElement;
        expect(component.ageIsError()).toBe(true);
        expect(ageError.innerText).toContain('Age has errored');
    });
}));

同样,实际实现在浏览器中有效,但单元测试失败。有谁知道如何在 Jasmine 中发出事件以将控件设置为脏状态,或者是否有更好的方法来实现这一点?谢谢!

最佳答案

在您的示例中 age.setValue(...) 实际上为输入设置了正确的值,但它没有将 ageError 附加到 DOM - 因为没有' 将控件标记为脏的真实/模拟事件。这就是方法 ageIsError 在这种情况下总是返回 false 的原因。

作为一种解决方法,我只是使用 document.createEvent('Event') 模拟了输入事件,看起来它工作正常:

  it('should show error message when age is less than 18', async(() => {
    const customEvent: Event = document.createEvent('Event');
    customEvent.initEvent('input', false, false);
    const ageInput = fixture.debugElement.query(By.css('input#age')).nativeElement;
    ageInput.value = 10;
    ageInput.dispatchEvent(customEvent);

    fixture.detectChanges();

    fixture.whenStable().then(() => {
      let ageError = fixture.debugElement.query(By.css('#ageError')).nativeElement;
      expect(component.ageIsError()).toBe(true);
      expect(ageError.innerText).toContain('Age has errored');
    });
  }));

我还找到了解决您的解决方案的方法 - 只需在 detectChanges 之前调用 age.markAsDirty():

  it('should show error message when age is less than 18', async(() => {
    let age = component.myForm.controls.age;
    age.setValue('10'); // { emitEvent: true } is by default
    age.markAsDirty(); // add this line

    fixture.detectChanges();

    fixture.whenStable().then(() => {
        let ageError = fixture.debugElement.query(By.css('#ageError')).nativeElement;
        expect(component.ageIsError()).toBe(true);
        expect(ageError.innerText).toContain('Age has errored');
    });
  }));

我还创建了一个 stackblitz example ,也请检查一下。希望这些解决方案对您有所帮助:)

关于angular - 为什么在 Jasmine Angular 单元测试中没有触发响应式(Reactive)表单控件的更改事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51866982/

相关文章:

angular - 如何在 Angular 2 中使用 ngModel 在多选中设置默认选定值

angular - ngrx 商店选择器在从自定义库导入应用程序时失败

angular - "You seem to not be depending on "@ Angular/核心 "."

javascript - 动态访问对象属性的 Typescript 类型推断

Angular 单元测试模拟 paramMap 获取

c# - 在 C# 中使用模拟测试成员分配

unit-testing - 使用 CDI-Unit Jglue 对 CDI 进行单元测试

css - 如何将 'required field' 星号添加到 Angular react 形式输入

typescript - TypeScript 中的循环类型引用

typescript - 为什么这段代码没有给我一个带有 --noImplicitAny 的错误?