Хотя ответы с наибольшим количеством голосов работают, они не демонстрируют хорошие методы тестирования, поэтому я подумал, что могу расширить ответ Гюнтера некоторыми практическими примерами.
Представим, что у нас есть следующий простой компонент:
@Component({
selector: 'my-demo',
template: `
<button (click)="buttonClicked()">Click Me!</button>
`
})
export class DemoComponent {
@Output() clicked = new EventEmitter<string>();
constructor() { }
buttonClicked(): void {
this.clicked.emit('clicked!');
}
}
Компонент - это тестируемая система, наблюдение за ее частями нарушает инкапсуляцию. Тесты компонентов Angular должны знать только три вещи:
- DOM (доступ, например, через
fixture.nativeElement.querySelector
);
- Имена
@Input
s и @Output
s; а также
- Сотрудничающие службы (введенные через систему DI).
Все, что связано с прямым вызовом методов в экземпляре или слежкой за частями компонента, слишком тесно связано с реализацией и добавит трение к рефакторингу - тестовые двойники должны использоваться только для соавторов. В этом случае, поскольку у нас нет соавторов, нам не нужны никакие моки, шпионы или другие тестовые двойники.
Один из способов проверить это - подписаться непосредственно на эмиттер, а затем вызвать действие щелчка (см. Компонент с входами и выходами ):
describe('DemoComponent', () => {
let component: DemoComponent;
let fixture: ComponentFixture<DemoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ DemoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DemoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should emit when clicked', () => {
let emitted: string;
component.clicked.subscribe((event: string) => {
emitted = event;
});
fixture.nativeElement.querySelector('button').click();
expect(emitted).toBe('clicked!');
});
});
Хотя это напрямую взаимодействует с экземпляром компонента, имя @Output
является частью общедоступного API, поэтому оно не слишком тесно связано.
В качестве альтернативы вы можете создать простой тестовый хост (см. Компонент внутри тестового хоста ) и фактически смонтировать свой компонент:
@Component({
selector: 'test-host',
template: `
<my-demo (clicked)="onClicked($event)"></my-demo>
`
})
class TestHostComponent {
lastClick = '';
onClicked(value: string): void {
this.lastClick = value;
}
}
затем протестируйте компонент в контексте:
describe('DemoComponent', () => {
let component: TestHostComponent;
let fixture: ComponentFixture<TestHostComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ TestHostComponent, DemoComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should emit when clicked', () => {
fixture.nativeElement.querySelector('button').click();
expect(component.lastClick).toBe('clicked!');
});
});
componentInstance
Вот тест хозяин , так что мы можем быть уверены в том, что мы не слишком связаны с компонентом , мы на самом деле тестирования.