角2 - 单元测试结合嵌套自定义窗体控件

问题描述:

我得从选择app-date-picker控件的自定义。它实现了ControlValueAccessor。我有一个名为MyPage组件包含通过这种自定义窗体控件:角2 - 单元测试结合嵌套自定义窗体控件

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

我试图写一个单元测试MyPage,测试的结合两个方向。我这样做对其他表单字段就好了,例如:当我为我的自定义窗体控件做到这一点

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); 
})); 

我的问题就出现了。到目前为止,我只能测试结合的一个方向,即便如此,它是需要使用的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单元测试结合两个方向,让我知道我正确连接起来,以表单控件
  2. 没有使用ng-reflect-*

其他说明:

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

定制表单字段正确实现ControlValueAccessor,具有date场,并且使用<input>内部,其本身是通过ngModel约束:

<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 
    } 
} 

UPDATE 8 /二千零十七分之十

到目前为止,我已经接近了什么,我想@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。与其说.nativeElement的,我可以打电话.componentInstance。然后测试变得简单:

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'); 
})); 

我还是如果任何人有一个更好的解决方案开放,但我会记住这为答案现在。希望它能帮助其他任何人遇到这个问题!