如何正确Mock document并编写visibilitychange事件的Jasmine单元测试?
Angular Jasmine测试中Mock Document的正确方式
你的当前做法不正确,直接修改window.document会报错是因为浏览器环境中window.document是只读属性(仅包含getter),无法直接赋值替换。正确的做法是利用Angular的依赖注入机制,通过DOCUMENT令牌提供Mock对象,而非直接操作全局window.document。
前提:组件中正确注入Document
确保组件通过Angular的DOCUMENT令牌注入,而非直接使用window.document,这是可测试性的基础:
import { DOCUMENT } from '@angular/common'; import { Component, Inject, OnInit } from '@angular/core'; @Component({ selector: 'app-your-component', template: '' }) export class YourComponent implements OnInit { constructor(@Inject(DOCUMENT) private document: Document) {} ngOnInit(): void { this.document.addEventListener('visibilitychange', () => this.checkSessionOnVisibilityChange()); } checkSessionOnVisibilityChange(): void { this.document.visibilityState === 'visible' ? this.callVisible() : this.reset(); } callVisible(): void { /* 业务逻辑 */ } reset(): void { /* 业务逻辑 */ } }
测试中的Mock实现步骤
创建Mock Document对象
模拟需要的属性(visibilityState)和方法(addEventListener/removeEventListener),用Jasmine spy监听方法调用:const mockDocument = { visibilityState: 'visible', addEventListener: jasmine.createSpy('addEventListener'), removeEventListener: jasmine.createSpy('removeEventListener') } as unknown as Document;在TestBed中注入Mock
在测试模块配置里,用useValue替换DOCUMENT的默认提供:import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DOCUMENT } from '@angular/common'; import { YourComponent } from './your.component'; describe('YourComponent', () => { let component: YourComponent; let fixture: ComponentFixture<YourComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [YourComponent], providers: [ { provide: DOCUMENT, useValue: mockDocument } ] }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(YourComponent); component = fixture.componentInstance; fixture.detectChanges(); // 触发ngOnInit }); });测试事件触发与方法调用
通过spy获取事件处理函数,手动触发并验证逻辑:it('should call callVisible when document is visible', () => { spyOn(component, 'callVisible'); // 获取visibilitychange的监听函数 const visibilityChangeHandler = mockDocument.addEventListener.calls.mostRecent().args[1]; mockDocument.visibilityState = 'visible'; visibilityChangeHandler(); // 手动触发事件 expect(component.callVisible).toHaveBeenCalled(); }); it('should call reset when document is hidden', () => { spyOn(component, 'reset'); const visibilityChangeHandler = mockDocument.addEventListener.calls.mostRecent().args[1]; mockDocument.visibilityState = 'hidden'; visibilityChangeHandler(); expect(component.reset).toHaveBeenCalled(); });
额外说明
如果你的组件目前直接使用window.document而非依赖注入,建议改为@Inject(DOCUMENT)的方式——这不仅能解决测试中的Mock问题,也符合Angular的依赖注入最佳实践,让组件更易维护和测试。
内容的提问来源于stack exchange,提问作者Niranjan
相关产品推荐
相关产品推荐

