我有一个替代方案来解决这个问题。如果你想知道为什么ShellZero的答案对我不起作用,请看下面。
我的解决方案是创建一个可注入的“包装器”服务,可以返回document
。这样,在生产中它会像往常一样获取文档,但在测试中,我可以模拟包装器并提供自己的“文档”并向其分派事件。
document-wrapper.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class DocumentWrapperService {
constructor() { }
public getDocument() {
return document;
}
}
在我的组件中,我可以在构造函数中注入此服务并从中获取
document
。在我的情况下,我在
ngOnInit
方法中使用它。
some.component.ts
import { DocumentWrapperService } from './../../../services/document-wrapper/document-wrapper.service';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'some-component',
template: '<div id="container"><!-- Your HTML here --></div>',
styleUrls: ['./some.component.css']
})
export class SomeComponent implements OnInit {
constructor(private documentWrapper: DocumentWrapperService) { }
ngOnInit() {
const _document = this.documentWrapper.getDocument();
_document.addEventListener('mouseup', ($event: MouseEvent) => {
});
}
这需要在测试中进行一些额外的工作。我必须制作一个模拟包装器并注入它。默认情况下,我的模拟返回document
,以便现有的测试不会出错。
import { DocumentWrapperService } from './../../../services/document-wrapper/document-wrapper.service';
import * as td from 'testdouble';
describe('SomeComponent', () => {
beforeEach(async(() => {
mockDocumentWrapper = td.object(DocumentWrapperService.prototype);
td.when(mockDocumentWrapper.getDocument()).thenReturn(document);
TestBed.configureTestingModule({
declarations: [SomeComponent],
providers: [
{ provide: DocumentWrapperService, useValue: mockDocumentWrapper }
]
})
.compileComponents();
}));
然后,在我的spec方法中,我正在测试事件处理,我必须设置我的模拟以返回一个不同的元素,而不是
document
。我找到的最好的方法是使用组件本身最外层的
div
。因为我的调用
addEventListener
在
ngOnInit
中,所以我还必须再次调用
ngOnInit
。一旦我完成了这个步骤,我就可以自由地分派事件并制定我的期望。
it("should do something when the user releases the mouse button", () => {
const rootDivElement = fixture.nativeElement.querySelector("#container");
td.when(mockDocumentWrapper.getDocument()).thenReturn(rootDivElement);
component.ngOnInit();
rootDivElement.dispatchEvent(new MouseEvent('mouseup', { clientY: 100, clientX: 200 }));
});
虽然ShellZero的回答是我能找到的最好的,但它并没有让我满意。在测试Angular组件上的事件处理程序时,我认为仅调用组件本身的处理程序方法是不足够的,因为这并不能证明组件已经正确地订阅了该事件。我更喜欢触发事件并期望组件正确地响应。
ShellZero回答中的“更新”部分在我实现时根本无法工作。我认为这是因为Karma将Angular组件放在一个没有访问根文档的iFrame中。如果我错了,我很想知道。
我不喜欢我的解决方案的唯一一件事是它添加了生产代码,这些代码仅在测试时才需要。通常情况下,我会更愿意在测试中跳过很多麻烦来避免为了测试而更改生产代码。在这种情况下,我看不到其他的解决办法。
this.callMouseMove()
的语法正确吗? - yurzuidocument.dispatchEvent(new Event("mousemove"))
会触发事件。 - yurzuiconst event = new Event("mousemove"); event.pageX = 250; document.dispatchEvent(event)
- yurzui