1

我得從選擇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'); 
})); 

任何人都知道一個更好的辦法?儘管這不是最佳的,但是現在我得到了。

回答

1

好了,終於找到了答案我很高興,因爲它避免了既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'); 
})); 

我還是如果任何人有一個更好的解決方案開放,但我會記住這爲答案現在。希望它能幫助其他任何人遇到這個問題!