Как изменить значение поля выбора в модульном тесте angular2?
у меня есть компонент Angular2, который содержит поле выбора, которое выглядит как
<select [(ngModel)]="envFilter" class="form-control" name="envSelector" (ngModelChange)="onChangeFilter($event)">
<option *ngFor="let env of envs" [ngValue]="env">{{env}}</option>
</select>
Я пытаюсь написать модульный тест для события ngModelChange. Это моя последняя неудачная попытка
it("should filter and show correct items", async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
el = fixture.debugElement.query(By.name("envSelector"));
fixture.detectChanges();
makeResponse([hist2, longhist]);
comp.envFilter = 'env3';
el.triggerEventHandler('change', {});
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(comp.displayedHistory).toEqual(longhist);
});
});
часть, с которой у меня возникли проблемы, заключается в том, что изменение значения базовой модели comp.envFilter = 'env3';
не вызывает метод изменения. Я добавил el.triggerEventHandler('change', {});
но это бросает Failed: Uncaught (in promise): ReferenceError: By is not defined
. Я не могу найти никаких намеков в документации... есть идеи?
3 ответов
что касается ошибки. Похоже, вам просто нужно импортировать By
. Это не что-то глобальное. Он должен быть импортирован из следующего модуля
import { By } from '@angular/platform-browser';
что касается части тестирования, это то, что я смог выяснить. При изменении значения в компоненте необходимо запустить обнаружение изменений для обновления представления. Вы делаете это с fixture.detectChanges()
. Как только это будет сделано,обычно представление должно быть обновлено с помощью значение.
из тестирования чего-то похожего на ваш пример, кажется, что это не так. Кажется, что после обнаружения изменений все еще существует некоторая асинхронная задача. Скажем, у нас есть следующее
const comp = fixture.componentInstance;
const select = fixture.debugElement.query(By.css('select'));
comp.selectedValue = 'a value;
fixture.DetectChanges();
expect(select.nativeElement.value).toEqual('1: a value');
кажется, это не работает. Похоже, что существует некоторая асинхронность, из-за которой значение еще не установлено. Поэтому нам нужно дождаться асинхронных задач, вызвав fixture.whenStable
comp.selectedValue = 'a value;
fixture.DetectChanges();
fixture.whenStable().then(() => {
expect(select.nativeElement.value).toEqual('1: a value');
});
выше будет работать. Но теперь нам нужно запустить изменить событие, поскольку это не происходит автоматически.
fixture.whenStable().then(() => {
expect(select.nativeElement.value).toEqual('1: a value');
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
fixture.whenStable().then(() => {
// component expectations here
});
});
теперь у нас есть еще одна асинхронная задача из события. Поэтому нам нужно стабилизировать его снова
ниже приведен полный тест, который я тестировал. Это рефакторинг примера из тесты интеграции исходного кода. Они использовали fakeAsync
и tick
который похож на использование async
и whenStable
. Но с fakeAsync
, вы не можете использовать templateUrl
, так что я, хотя было бы лучше, чтобы перепроектировать ее использовать async
.
также тесты исходного кода делают вид двойного одностороннего тестирования, сначала тестируя модель для просмотра, а затем для просмотра модели. Хотя похоже, что ваш тест пытался сделать своего рода двусторонний тест, от модели к модели. Так что я рефакторинг это немного в вашем примере люкс лучше.
import { Component } from '@angular/core';
import { TestBed, getTestBed, async } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { dispatchEvent } from '@angular/platform-browser/testing/browser_util';
@Component({
selector: 'ng-model-select-form',
template: `
<select [(ngModel)]="selectedCity" (ngModelChange)="onSelected($event)">
<option *ngFor="let c of cities" [ngValue]="c"> {{c.name}} </option>
</select>
`
})
class NgModelSelectForm {
selectedCity: {[k: string]: string} = {};
cities: any[] = [];
onSelected(value) {
}
}
describe('component: NgModelSelectForm', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ FormsModule ],
declarations: [ NgModelSelectForm ]
});
});
it('should go from model to change event', async(() => {
const fixture = TestBed.createComponent(NgModelSelectForm);
const comp = fixture.componentInstance;
spyOn(comp, 'onSelected');
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
comp.selectedCity = comp.cities[1];
fixture.detectChanges();
const select = fixture.debugElement.query(By.css('select'));
fixture.whenStable().then(() => {
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(comp.onSelected).toHaveBeenCalledWith({name : 'NYC'});
console.log('after expect NYC');
});
});
}));
});
посмотрите этот пример из углового источника (template_integration_spec.ts)
@Component({
selector: 'ng-model-select-form',
template: `
<select [(ngModel)]="selectedCity">
<option *ngFor="let c of cities" [ngValue]="c"> {{c.name}} </option>
</select>
`
})
class NgModelSelectForm {
selectedCity: {[k: string]: string} = {};
cities: any[] = [];
}
it('with option values that are objects', fakeAsync(() => {
const fixture = TestBed.createComponent(NgModelSelectForm);
const comp = fixture.componentInstance;
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
comp.selectedCity = comp.cities[1];
fixture.detectChanges();
tick();
const select = fixture.debugElement.query(By.css('select'));
const nycOption = fixture.debugElement.queryAll(By.css('option'))[1];
// model -> view
expect(select.nativeElement.value).toEqual('1: Object');
expect(nycOption.nativeElement.selected).toBe(true);
select.nativeElement.value = '2: Object';
dispatchEvent(select.nativeElement, 'change');
fixture.detectChanges();
tick();
// view -> model
expect(comp.selectedCity['name']).toEqual('Buffalo');
}));
Я нашел ответ peeskillet очень полезным, но, к сожалению, он немного устарел, поскольку способ отправки события был изменен. Я также обнаружил, что был ненужный вызов whenStable (). Итак, вот обновленный тест с использованием установки peeskillet:
it('should go from model to change event', async(() => {
const fixture = TestBed.createComponent(NgModelSelectForm);
const comp = fixture.componentInstance;
spyOn(comp, 'onSelected');
comp.cities = [{'name': 'SF'}, {'name': 'NYC'}, {'name': 'Buffalo'}];
comp.selectedCity = comp.cities[1];
fixture.detectChanges();
const select = fixture.debugElement.query(By.css('select'));
fixture.whenStable().then(() => {
select.nativeElement.dispatchEvent(new Event('change'));
fixture.detectChanges();
expect(comp.onSelected).toHaveBeenCalledWith({name : 'NYC'});
console.log('after expect NYC');
});
}));