我有一个使用EventEmitter的组件,单击页面上的某人时将使用EventEmitter。有什么方法可以在单元测试期间观察EventEmitter,并使用TestComponentBuilder单击触发EventEmitter.next()方法的元素,并查看发送了什么?
Answers:
您的测试可能是:
it('should emit on click', () => {
const fixture = TestBed.createComponent(MyComponent);
// spy on event emitter
const component = fixture.componentInstance;
spyOn(component.myEventEmitter, 'emit');
// trigger the click
const nativeElement = fixture.nativeElement;
const button = nativeElement.querySelector('button');
button.dispatchEvent(new Event('click'));
fixture.detectChanges();
expect(component.myEventEmitter.emit).toHaveBeenCalledWith('hello');
});
当您的组件是:
@Component({ ... })
class MyComponent {
@Output myEventEmitter = new EventEmitter<string>();
buttonClick() {
this.myEventEmitter.emit('hello');
}
}
<my-component (myEventEmitter)="function($event)"></my-component>
,在测试中,我这样做:tcb.overrideTemplate(TestComponent,html).createAsync(TestComponent)
您可以使用间谍,取决于您的风格。这是您轻松使用间谍来查看是否emit
被解雇的方法...
it('should emit on click', () => {
spyOn(component.eventEmitter, 'emit');
component.buttonClick();
expect(component.eventEmitter.emit).toHaveBeenCalled();
expect(component.eventEmitter.emit).toHaveBeenCalledWith('bar');
});
expect
是实际的间谍(spyOn()
通话结果)吗?
您可以@Output()
在父模板中订阅或绑定发射器(如果它是),并在父组件中检查绑定是否已更新。您还可以调度click事件,然后应触发订阅。
TestComponent
has中的模板<my-component (someEmitter)="value=$event">
(其中someEmitter
是@Output()
)value
,TextComponent
则应使用sendd事件来更新的属性。
尽管票数最高的答案行之有效,但它们并没有表现出良好的测试实践,因此我认为我将通过一些实际示例来扩展Günter的答案。
假设我们有以下简单组件:
@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!');
}
}
该组件是被测系统,监视它的一部分会破坏封装。角组件测试应该只了解三件事:
fixture.nativeElement.querySelector
);@Input
s和@Output
s的名称;和涉及直接在实例上调用方法或监视组件的某些部分的任何事情都与实现紧密联系在一起,并且会增加重构的难度-测试双打仅应用于协作者。在这种情况下,因为我们没有合作者,所以我们不需要任何模拟,间谍或其他测试双打。
一种测试方法是直接订阅发射器,然后调用click操作(请参阅具有输入和输出的组件):
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)并实际安装您的组件:
@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
是测试主机,因此我们可以确信我们不会过度耦合到实际测试的组件。