测试无法在模板驱动的表单中识别默认属性`form.invalid`

问题描述

我想测试一个模板驱动的表单。最初,我希望禁用表单的按钮,因为我想确保用户输入名称。这是通过名称输入字段上的'required'属性完成的。

所以我创建了以下模板:

<form #form="ngForm">
  <!-- note the 'required' property on the input field -->
  <input
    type="text"
    name="name"
    id="name"
    #name="ngModel"
    required
    ngModel
  />
  <button type="submit" [disabled]="form.invalid">Submit</button>
  {{ form.invalid | json }}
</form>

这在浏览器中呈现得很好:

enter image description here

现在,我想测试这种行为。这就是时髦的地方。测试如下:

import { async,ComponentFixture,Testbed } from "@angular/core/testing";
import { AppComponent } from "./app.component";
import { DebugElement } from "@angular/core";
import { By } from "@angular/platform-browser";
import { FormsModule } from "@angular/forms";
import { CommonModule } from "@angular/common";

describe("AppComponent",() => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let el: DebugElement;
  let button: HTMLButtonElement;

  beforeEach(async(() => {
    Testbed.configureTestingModule({
      declarations: [AppComponent],imports: [CommonModule,FormsModule]
    }).compileComponents();
  }));
  beforeEach(() => {
    fixture = Testbed.createComponent(AppComponent);
    component = fixture.componentInstance;
    el = fixture.debugElement;
    fixture.detectChanges();
    button = el.query(By.css("button")).nativeElement;
  });
  describe("submit button",() => {
    it("should be disabled by default",() => {
      expect(button.nativeNode.disabled).toBeTrue();
    });
  });
});

我的测试失败,并且无法识别form.invalid属性。它将其设置为“ false”:

enter image description here

所以我最好的猜测是,如果输入值之一不符合要求,Angular应用程序的实际'ng build'或'ng serve'会做一些魔术,将ngForm设置为具有无效属性。 / p>

但是我该如何在测试中做到这一点?

我希望测试尽可能接近真实示例。因此,必须手动将form.invalid设置为true会破坏测试的目的。

这是上述代码https://stackblitz.com/edit/angular-ivy-pfnkfc

的Stackblitz示例

解决方法

因此,似乎有一些异步任务需要完成(我们等待fixture.whenStable,然后需要调用fixture.detectChanges()。这是我几乎总是使用反应形式的原因之一,因为它更容易测试。您可以仅通过TypeScript代码进行测试,而不必完全查看HTML。

import { async,ComponentFixture,TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

describe('AppComponent',() => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let el: DebugElement;
  let button: HTMLButtonElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [AppComponent],imports: [FormsModule]
    }).compileComponents();
  }));
  beforeEach(async () => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    el = fixture.debugElement;
    fixture.detectChanges();
  });

  describe('submit button',() => {
    it('should be disabled by default',async () => {
      await fixture.whenStable(); // wait for all asynchronous tasks to complete
      fixture.detectChanges(); // call detectChanges again
      button = fixture.debugElement.query(By.css('button')).nativeElement;
      expect(button.disabled).toBe(true);
    });
  });
});

注意事项:

1。)每次调用fixture.detectChanges时,都需要重新引用fixture.debugElement.query...,因为每次调用fixture.detectChanges时视图都会改变。如果我们要像在el中一样继续使用beforeEach,那么您将对HTML的外观有旧的了解。

2。)您无需在Angular单元测试中导入CommonModule