使用Angular组件(sample2 / Template driven Form)进行测试的方法。测试Angular组件(使用sample2 / 模板驱动表单)的方法

目录:Angular 组件的测试


在Angular中,有几种实现表单的方法,但在本章中,我们将介绍基本的基于模板的表单的测试方法。

模板驱动表单是Angular中的一种表单处理方式。它可以使用模板来定义表单的结构和验证规则。具体的使用方法可以在Angular的官方文档中找到。

本文参考自下列测试代码。

Angular v5.2.8 模板集成测试代码
https://github.com/angular/angular/blob/5.2.8/packages/forms/test/template_integration_spec.ts

创建组件

首先,创建一个用于编写测试的组件。

$ ng generate component sample2

在创建的空组件中,设置以下模板和属性。
HTML元素与上一章中的Sample1Component相同,但有一个不同之处在于将整个元素用

标签包围起来。

// sample2.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-sample2',
  templateUrl: './sample2.component.html',
  styleUrls: ['./sample2.component.css']
})
export class Sample2Component implements OnInit {
  id: number;
  name: string;
  agreement: boolean;
  gender: string;
  works = [
    { id: 'employee', label: '会社員' },
    { id: 'student', label: '学生' },
    { id: 'other', label: 'その他' },
  ];
  work: { [key: string]: string };
  note: string;

  ...
}
// sample2.component.html

<form>
  <span name="id">{{id}}</span>

  <input name="name" type="text" [(ngModel)]="name">

  <input type="checkbox" name="agreement" [(ngModel)]="agreement">

  <input type="radio" name="gender" value="male" [(ngModel)]="gender"><input type="radio" name="gender" value="female" [(ngModel)]="gender"><select name="work" [(ngModel)]="work">
    <option *ngFor="let item of works" [ngValue]="item">{{item.label}}</option>
  </select>

  <textarea name="note" [(ngModel)]="note"></textarea>
</form>
// app.module.ts

+ import { FormsModule } from '@angular/forms';

@NgModule({
  declarations: [
    ...
  ],
  imports: [
+    FormsModule,
  ],
  ...

为进行组件测试做准备。

表单中使用了ngForm指令,该指令在模板中定义。
由于这些指令是在FormsModule中定义的,因此在测试环境中也需要加载该模块。

Angular v5.2.8 form_providers.ts
https://github.com/angular/angular/blob/5.2.8/packages/forms/src/form_providers.ts#L16-L24
https://github.com/angular/angular/blob/5.2.8/packages/forms/src/directives.ts#L68

Angular v5.2.8 form_providers.ts
https://github.com/angular/angular/blob/5.2.8/packages/forms/src/form_providers.ts 第16行至第24行
https://github.com/angular/angular/blob/5.2.8/packages/forms/src/directives.ts 第68行

// sample2.component.spec.ts

import {
  async,
  ComponentFixture,
+  fakeAsync,
  TestBed,
+  tick
} from '@angular/core/testing';

+ import {
+   FormsModule,
+   NgForm
+ } from '@angular/forms';

+ import { By } from '@angular/platform-browser';

describe('Sample2Component', () => {
  let component: Sample2Component;
  let fixture: ComponentFixture<Sample2Component>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
+      imports: [FormsModule],
      declarations: [ Sample2Component ]
    })
    .compileComponents();
  }));

验证组件的属性是否在模板中反映出来

我将使用添加到模板的来进行验证。

// sample2.component.spec.ts

  beforeEach(() => {
    fixture = TestBed.createComponent(Sample2Component);
    component = fixture.componentInstance;
-    fixture.detectChanges(); // ①
  });

  ...

+  it('input - コンポーネントのプロパティがフォームに反映される', fakeAsync(() => {
+    component.name = 'abc'; // ②
+    fixture.detectChanges(); // ③
+    tick();
+
+    const element = fixture.debugElement.query(By.css('[name=name]')).nativeElement as HTMLInputElement;
+    expect(element.value).toBe('abc');
+  }));

这个也和在 Sample1Component 中写的测试几乎是一样的,只有一个不同之处。
在 beforeEach 中调用 fixture.detectChanges() 后(①),修改了组件的属性(②),再次调用 fixture.detectChanges() 后(③),模板没有更新。
因此,我们将 fixture.detectChanges() 的调用方式修改为只在测试套件内部调用。

通过表单进行验证

如果在模板中使用

标签,Angular会自动应用NgForm指令。
在模板中,可以使用
来引用与NgForm指令相关联的类实例。
如果想要在测试代码中引用相同的表单,可以使用injector。

// sample2.component.spec.ts

+  function getForm() {
+    return fixture.debugElement.children[0].injector.get(NgForm);
+  }

  ...

  it('input コンポーネントのプロパティがフォームに反映される', fakeAsync(() => {
    component.name = 'abc';
    fixture.detectChanges();
    tick();

    const element = fixture.debugElement.query(By.css('[name=name]')).nativeElement as HTMLInputElement;
    expect(element.value).toBe('abc');
+    expect(getForm().value.name).toBe('abc');
  }));

为了验证表单的值,可以使用value属性。
除了值以外,还可以引用其他与表单特定属性相关的验证结果等,这样可以增加测试的方法。

验证更改表单值时组件属性发生变化的情况。

最开始,需要使用fakeAsync / tick来完成ngModel的异步处理。

// sample2.component.spec.ts

+ it('input フォームの値の変更がコンポーネントに反映される', fakeAsync(() => {
+   fixture.detectChanges();
+   tick();
+
+   const element = fixture.debugElement.query(By.css('[name=name]')).nativeElement as HTMLInputElement;
+   element.value = 'def';
+   element.dispatchEvent(new Event('input'));
+
+   expect(component.name).toBe('def');
+   expect(getForm().value.name).toBe('def');
+ }));

验证

可以使用与HTML5验证相同的表示方法在表单中添加验证的模板驱动形式。您可以使用required属性来编写测试。

// sample2.component.html

<form>
...
-   <input name="name" type="text" [(ngModel)]="name">
+   <input name="name" type="text" [(ngModel)]="name" required>
// sample2.component.spec.ts

+ it('input フォームのバリデーション', fakeAsync(() => {
+   fixture.detectChanges();
+   tick();
+
+   const element = fixture.debugElement.query(By.css('[name=name]')).nativeElement as HTMLInputElement;
+
+   element.value = '';
+   element.dispatchEvent(new Event('input'));
+   expect(getForm().valid).toBe(false);
+
+   element.value = 'abc';
+   element.dispatchEvent(new Event('input'));
+   expect(getForm().valid).toBe(true);
}));

在实际运用中,仅仅使用HTML5的验证是不够的,可能会出现需要更复杂操作的情况。Angular建议在这种情况下使用Reactive-forms,而不是Template driven Form。


以下是使用Reactive form测试组件的示例3的文章。

bannerAds