如何在角度组件中测试ViewChild / ContentChild属性?

时间:2018-01-14 18:26:42

标签: angular unit-testing typescript

我有一个表单容器,可以正确显示验证错误。我通过ContentChild装饰器访问表单控件,并以反应方式操作它以构建验证消息。

我的问题是:我怎样才能对这样的组件进行适当的单元测试?

component.ts

import { Component, ContentChild, Input, AfterContentInit } from '@angular/core';
import { FormControlName } from '@angular/forms';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/map';

@Component({
  selector: 'app-form-group',
  templateUrl: './form-group.component.html',
  styleUrls: ['./form-group.component.scss'],
})
export class FormGroupComponent implements AfterContentInit {

  @Input()
  errorMessages: { [key: string]: string } = { };

  @ContentChild(FormControlName)
  control: FormControlName;

  message: Observable<string>;
  success: Observable<boolean>;
  error: Observable<boolean>;

  private buildErrorMessage(status): string {
    if (status === 'INVALID' && this.control.touched && this.control.dirty) {
      return Object.keys(this.control.errors)
        .map(errorKey => this.errorMessages[errorKey])
        .join('\n');
    }
  }

  ngAfterContentInit() {
    const delayedStatusChanges = this.control.statusChanges
      .debounceTime(500);

    this.message = delayedStatusChanges
      .map(status => this.buildErrorMessage(status));
    this.success = delayedStatusChanges
      .map(status => status === 'VALID' && this.control.touched && this.control.dirty);
    this.error = delayedStatusChanges
      .map(status => status === 'INVALID' && this.control.touched && this.control.dirty);
  }
}

我只是使用状态更改来更新我的样式。还要在发生故障时显示所有验证消息。

component.html

<div
  class="form-group"
  [class.has-success]="success | async"
  [class.has-error]="error | async"
>

  <ng-content></ng-content>

  <div
    class="help-block"
    [hidden]="message | async"
  >
    <i class="fa fa-remove"></i>
    {{ message | async }}
  </div>

</div>

component.spec.ts

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

import { FormGroupComponent } from './form-group.component';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        FormGroupComponent,
      ],
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(FormGroupComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

1 个答案:

答案 0 :(得分:3)

为了向测试组件提供内容投影,可以在另一个包装器组件中编译它。

考虑到所需的ContentChild指令(FormControlName)可以在没有表格组合的情况下存在:

@Component({
  template: `<app-form-group><input [formControlName]></app-form-group>`
})
class WrapperComponent {}

试验台应包括指令和组件:

TestBed.configureTestingModule({
  declarations: [FormGroupComponent, WrapperComponent, FormControlName]
})

预计该属性将是FormControlName指令类的实例,可以查询和断言已测试的组件:

fixture = TestBed.createComponent(WrapperComponent);
fixture.detectChanges();

const wrapperCompDE = fixture.debugElement;
const testedCompDE = wrapperCompDE.query(By.directive(FormGroupComponent));
const testedComp = testedCompDE.componentInstance;
const inputDE = testedCompDE.query(By.css('input'));
const contentChild = inputDE.injector.get(FormControlName);

expect(contentChild instanceof FormControlName).toBe(true);
expect(testedComp.control).toBe(contentChild);

可以添加中介断言。

相关问题