我有一个使用EventEmitter的组件,当点击页面上的某个人时会使用EventEmitter。有没有办法在单元测试期间观察EventEmitter,并使用TestComponentBuilder单击触发EventEmitter.next()方法的元素并查看发送的内容?
答案 0 :(得分:111)
您的测试可能是:
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');
}
}
答案 1 :(得分:27)
假设您有一个名为buttonClick()
的方法,它触发一个名为eventEmitter
的事件发射器,您可以订阅该事件,然后调用该方法以查看该事件是否触发,并调用该回调done()
什么时候......
@Component({ ... })
class MyComponent {
@Output() eventEmitter = new EventEmitter();
buttonClick() {
this.eventEmitter.emit('bar');
}
}
然后在测试中......
it('should emit on click', (done) => {
component.eventEmitter.subscribe(foo => {
expect(foo).toEqual('bar');
done();
});
component.buttonClick();
});
或者,你可以使用间谍,取决于你的风格。以下是如何使用间谍轻松查看emit
是否被解雇......
it('should emit on click', () => {
spyOn(component.eventEmitter, 'emit');
component.buttonClick();
expect(component.eventEmitter.emit).toHaveBeenCalled();
expect(component.eventEmitter.emit).toHaveBeenCalledWith('bar');
});
答案 2 :(得分:3)
您可以在父模板中订阅发射器或绑定它(如果它是@Output()
,并检查父组件是否更新了绑定。
您也可以发送点击事件,然后触发订阅。
答案 3 :(得分:0)
我需要测试发射数组的长度。因此,这就是我在其他答案之上进行的操作。
expect(component.myEmitter.emit).toHaveBeenCalledWith([anything(), anything()]);
答案 4 :(得分:-1)
尽管获得最高投票的答案有效,但是它们并没有证明良好的测试实践,因此我想我将通过一些实际示例来扩展Günter's answer。
假设我们有以下简单组件:
@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
和@Output
的名称;和涉及直接在实例上调用方法或监视组件的某些部分的任何事情都与实现紧密联系在一起,并且会增加重构的难度-双重测试仅应用于协作者。在这种情况下,因为我们没有合作者,所以我们不需要任何模拟,间谍或其他测试双打。
一种测试方法是直接订阅发射器,然后调用click操作(请参见Component with inputs and outputs):
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 inside a test host)并实际安装您的组件:
@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
是测试主机,因此我们可以确定我们并没有过度耦合到实际测试的组件。