mock window.addEventListener() method



如何测试这段代码:

this.window.addEventListener('pathSelectedFromMfe', (customEvent: CustomEvent) => {
this.window.location.href = customEvent.detail.path;
});

这是我尝试过的:

假设您想在收到事件时测试window.addEventListener()方法而不修改window.location.href属性。通常,一种简单的方法是遵循以下答案:如何测试angular事件监听器?如您所见,在这种情况下不需要mock。

如果你对我的代码继续前面的解决方案,测试将失败,出现如下错误:Some of your tests did a full page reload!.

我的下一步是找出如何测试this.window.location.href = customEvent.detail.path;行,这可以通过以下答案来完成:Angular单元测试窗口。如您所见,在本例中使用了一个mock。

现在我相信这个解决方案(通过使用模拟)可以修改,也可以测试window.addEventListener()方法,但我不确定如何做到这一点。

所以我找到了一个解决方案,但我不确定它是否是最优的。基本上,我所做的是模拟window.addEventListener()方法,但这意味着我不能在我的测试中再使用window.dispatchEvent()方法了(也许这个方法也可以被嘲笑,但我不知道在这种情况下它们如何协同工作)。这些是我必须创建/修改的文件来测试我的代码:

window-token.ts

import { InjectionToken } from '@angular/core';
export const WindowToken = new InjectionToken('Window');
export function windowProvider() {
return window;
}

app.module.ts

将这行添加到providers数组中:

{ provide: WindowToken, useFactory: windowProvider }

my.component.ts

import { Component, Inject, ViewEncapsulation } from '@angular/core';
import { WindowToken } from './core/window-token/window-token';
@Component({
selector: 'app-my',
templateUrl: './my.component.html',
styleUrls: ['./my.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class MyComponent {
constructor(@Inject(WindowToken) private window) {
this.window.addEventListener('pathSelectedFromMfe', (customEvent: CustomEvent) => {
this.window.location.href = customEvent.detail.path;
});
}
}

my.component.spec.ts

import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { WindowToken } from './core/window-token/window-token';
import { MyComponent } from './my.component';
describe('MyComponent', () => {
let fixture: ComponentFixture<MyComponent>;
let customPath = '';
const windowMock = {
location: {
_href: '',
set href(url: string) {
this._href = url;
},
get href() {
return this._href;
}
},
addEventListener(event: string, callback: (customEvent: CustomEvent) => void) {
callback(new CustomEvent(event, { detail: { path: customPath } }));
}
};
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [MyComponent],
providers: [
{ provide: WindowToken, useValue: windowMock }
]
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
fixture.detectChanges();
});
it('should handle pathSelectedFromMfe window event', () => {
const setHrefSpy = spyOnProperty(windowMock.location, 'href', 'set');
customPath = '/search';
// the constructor is called when we create the MyComponent, so we invoke the creation again to use the new value for customPath
TestBed.createComponent(MyComponent);
expect(setHrefSpy).toHaveBeenCalledWith('/search');
});
});

我不认为这是最好的解决方案,因为当我们调用TestBed.createComponent(MyComponent);时,constructor代码会在单元测试的beforeEach()方法中直接调用。这意味着我们在单元测试开始之前调用this.window.location.href = ''

每次我们想要用收到的事件的不同值测试代码时,我们需要调用TestBed.createComponent(MyComponent);,而当使用真正的窗口对象时(就像在我最初的问题消息中链接的解决方案中),我们只需要调度一个新事件。在此解决方案中,还必须通过辅助变量修改事件,在我的情况下是customPath字符串。

目前这个解决方案适用于我,但如果有人找到一个更好的,请添加一个响应。

PS:该代码是Angular 13项目的一部分。

相关内容

最新更新