【问题标题】:Why is change event for reactive form control not firing in Jasmine Angular unit test?为什么在 Jasmine Angular 单元测试中没有触发响应式表单控件的更改事件?
【发布时间】:2019-01-22 19:40:16
【问题描述】:

我有一个 change 函数,我将传递给一个 反应式表单控件 的 change 事件,它评估 dirty 状态并检查控件上是否有任何错误如果是这样,则将布尔标志设置为真/假。然后使用此布尔标志来确定是否显示具有错误消息的<div> 元素。这在浏览器中工作得很好,但是当单元测试运行时,“脏”永远不会被设置为 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');
    });
}));

同样,实际实现在浏览器中工作,但单元测试失败。有谁知道在茉莉花中发出事件以将控件设置为脏状态,还是有更好的方法来实现这一点?谢谢!

【问题讨论】:

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


    【解决方案1】:

    在您的示例中,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,也请检查一下。希望这些解决方案对您有所帮助:)

    【讨论】:

      【解决方案2】:

      表单控件绑定会自动更新。

      下面的代码在一个测试块中。它只是获取一个输入字段并在其上分派一个更新值。

      然后我渲染出控制器状态并确认绑定自动工作。

        const usernameInput: HTMLInputElement = fixture.nativeElement.querySelector(
                  '#username'
              );
              usernameInput.value = 'testValueNotEmail';
              usernameInput.dispatchEvent(new Event('input'));
      
              // FormGroup and Controll Bindings and Validations were automatically updated after the event.
              console.log(component.loginForm.getRawValue());
              console.log(component.loginForm.get('username')?.errors);
              console.log(component.loginForm.get('username')?.touched);
              console.log(component.loginForm.get('username')?.dirty);
      
              // In order for the fixture render element changes to take effect,
              // I had to detect the changes. The form error fields were then rendered. 
              fixture.detectChanges();
              const errorField = fixture.nativeElement.querySelector(
                  '#usernameSmall'
              );
              console.log(errorField);
      

      【讨论】:

        猜你喜欢
        • 2021-01-02
        • 2019-01-28
        • 1970-01-01
        • 1970-01-01
        • 2017-12-15
        • 1970-01-01
        • 2014-09-02
        • 2018-01-08
        • 1970-01-01
        相关资源
        最近更新 更多