Angular 2 - 绑定(bind)到嵌套自定义表单控件的单元测试

标签 angular unit-testing angular2-forms angular2-testing angular-forms

我有一个来自控件的自定义选择器 app-date-picker .它实现了 ControlValueAccessor .我有一个名为 MyPage 的组件通过以下方式包含此自定义表单控件:

<app-date-picker class="address__from-date" [(ngModel)]="fromDate"></app-date-picker>

我正在尝试为 MyPage 编写单元测试测试绑定(bind)的两个方向。我已经为其他表单字段完成了此操作,例如:

it('should bind zip code', fakeAsync(() => {
  const formControl = element.query(By.css('.address__zip-code input')).nativeElement;

  // model -> view
  component.zipCode = 76777;
  fixture.detectChanges();
  tick();

  expect(formControl.value).toEqual('76777');

  // view -> model
  formControl.value = '89556';
  formControl.dispatchEvent(new Event('input'));

  expect(component.zipCode).toEqual(89556);
}));

当我尝试为我的自定义表单控件执行此操作时,我的问题出现了。到目前为止,我只能测试一个方向的绑定(bind),即使这样也需要使用 ng-reflect-model ,这太糟糕了:

it('should bind from-date', fakeAsync(() => {
  const formControl = element.query(By.css('.address__from-date app-date-picker')).nativeElement;

  // model -> view
  component.fromDate = '01/2017';
  fixture.detectChanges();
  tick();

  expect(formControl.attributes['ng-reflect-model'].value).toEqual('01/2017');

  // view -> model
  // Not sure what to do here either
}));

有没有更好的方法来做到这一点?我想:

  1. 能够测试来自 MyPage 的双向绑定(bind)单元测试,以便我知道我已将它正确连接到表单控件
  2. 不必使用 ng-reflect-*

其他注意事项:

MyPage组件是带有 fromDate 的标准组件(和一个 zipCode )字段。

自定义表单域实现ControlValueAccessor正确,有一个 date字段,并使用 <input>在内部,它本身通过 ngModel 绑定(bind):

<input [(ngModel)]="date" type="date">

DatePickerComponent

@Component({
  selector: 'app-date-picker',
  templateUrl: './date-picker.component.html',
  styleUrls: ['./date-picker.component.scss'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => DatePickerComponent),
    multi: true
  }]
})
export class DatePickerComponent implements ControlValueAccessor {

  private propagateChange = (_: any) => {};

  private _date: string;
  get date(): string {
    return this._date;
  }

  @Input()
  set date(value: string) {
    this._date = value;
    this.propagateChange(value);
  }

  writeValue(newValue: any) {
    if (newValue !== undefined) {
      this.date = newValue;
    }
  }

  registerOnChange(fn: any) {
    this.propagateChange = fn;
  }

  registerOnTouched(fn: any) {
    // Not used
  }
}

2017 年 8 月 10 日更新

到目前为止,我已经接近我想要的 @ViewChild :

@Component(...)
MyPageComponent {
 @ViewChild('fromDate') fromDatePicker: DatePickerComponent;
 ...
}

MyPage模板变为(注意模板引用变量 #fromDate ):

<app-date-picker #fromDate class="address__from-date" [(ngModel)]="fromDate">
</app-date-picker>

那么测试就变成了:

it('should bind from-date', fakeAsync(() => {
  // model -> view
  component.info.fromDate = '01/2017';
  fixture.detectChanges();
  tick();

  expect(component.fromDatePicker.date).toEqual('01/2017');

  // view -> model
  component.fromDatePicker.date = '02/2017';

  expect(component.info.fromDate).toEqual('02/2017');
}));

有人知道更好的方法吗?虽然这不是最佳选择,但我暂时还可以。

最佳答案

好吧,终于找到了一个令我满意的答案,因为它避免了 ng-reflect-*@ViewChild。我可以调用 .componentInstance,而不是调用 .nativeElement。那么测试就变得简单了:

it('should bind from-date', fakeAsync(() => {
  const formControl = element.query(By.css('.address__from-date app-date-picker')).componentInstance;

  // model -> view
  component.fromDate = '01/2017';
  fixture.detectChanges();
  tick();

  expect(formControl.date).toEqual('01/2017');

  // view -> model
  formControl.date = '02/2017';

  expect(component.fromDate).toEqual('02/2017');
}));

如果有人有更好的解决方案,我仍然持开放态度,但我暂时将其标记为答案。希望它能帮助遇到此问题的其他人!

关于Angular 2 - 绑定(bind)到嵌套自定义表单控件的单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45601561/

相关文章:

javascript - Angular 2 Observable 和 Promise 回调单元测试

c# - 如何强制单元测试的状态为 "Test wasn' t run”?(MS 测试)

jquery - 如何在 Angular 4 中使用 jQuery 插件?

javascript - Angular:垃圾收集组件

javascript - 如何以正确的方式对 MatDialog(Angular Material)进行单元测试?

javascript - angular js Controller 中的单元测试观察者

javascript - 我们如何在 Angular 2 中编写基本单元测试?

angular - 如何在 Angular 2 中进行全局搜索?

angular - 如何在 Angular 2 中预填充数据?使用表单组?

Angular 2+ Material mat-chip-list formArray验证