【问题标题】:How to test a ViewChild/ContentChild property in a angular component?如何在角度组件中测试 ViewChild/ContentChild 属性?
【发布时间】:2018-06-23 11:31:54
【问题描述】:

我有一个用于正确显示验证错误的表单容器。我通过 ContentChild 装饰器访问表单控件,并对其进行反应性操作以构建验证消息。

我的问题是:如何正确地对这样的组件进行单元测试?

组件.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();
  });
});

【问题讨论】:

  • 到目前为止你编写的单元测试在哪里?
  • 问题也不包含模板。
  • 我已经添加了模板和基本规范。我希望测试是否正确添加了 css 类,并测试是否显示了正确的验证消息。
  • 这是针对不同测试的不同任务。您可以将伪造的control 对象分配给组件实例并根据您的期望进行测试。

标签: angular unit-testing typescript


【解决方案1】:

为了给被测组件提供内容投影,可以在另一个包装器组件中进行编译。

考虑到所需的 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);

可以根据口味添加中间断言。

【讨论】:

    猜你喜欢
    • 2018-12-02
    • 1970-01-01
    • 2021-03-11
    • 2016-05-16
    • 2020-10-14
    • 1970-01-01
    • 2022-01-10
    • 2016-03-23
    • 2019-06-05
    相关资源
    最近更新 更多