如何在Jest中mock document.activeElement以测试DOM焦点切换逻辑?
如何测试依赖document.activeElement的DOM函数分支
嘿,这个问题我之前踩过坑!document.activeElement是个只读属性(只有getter没有setter),所以你直接赋值global.document.activeElement = ...才会抛那个错误。要验证this.lastTabStop.focus()是否被调用,得通过重定义它的getter来模拟值,下面是具体的实现方案(以Jest为例,其他测试框架思路一致):
1. 先模拟需要的DOM元素
首先得创建模拟的firstTabStop和lastTabStop,重点是给lastTabStop加一个可跟踪的focus方法,方便后面断言:
// 模拟第一个tab元素 const mockFirstTabStop = {}; // 模拟最后一个tab元素,用jest.fn()创建可监控的focus方法 const mockLastTabStop = { focus: jest.fn() };
2. 重定义document.activeElement的getter
用Object.defineProperty来覆盖原生的activeElement getter,让它返回我们的模拟元素:
Object.defineProperty(document, 'activeElement', { // 每次获取activeElement时返回mockFirstTabStop get: () => mockFirstTabStop, // 设为可配置,这样测试结束后能恢复,避免污染其他测试 configurable: true });
3. 绑定上下文并调用init方法
因为trap.init里的this指向trap对象本身,所以要确保trap的firstTabStop和lastTabStop指向我们的模拟对象,然后调用init:
// 导入你的trap模块 const trapInit = require('./path-to-your-trap-module'); // 构建trap实例,关联模拟元素 const trap = { firstTabStop: mockFirstTabStop, lastTabStop: mockLastTabStop, init: trapInit }; // 调用要测试的init方法 trap.init();
4. 断言focus方法被正确调用
最后验证lastTabStop.focus是否被调用了一次:
expect(mockLastTabStop.focus).toHaveBeenCalledTimes(1);
额外提示:测试后清理
为了不影响其他测试用例,记得在测试结束后恢复document.activeElement的原始状态:
afterEach(() => { // 恢复activeElement到默认值(一般是document.body) Object.defineProperty(document, 'activeElement', { get: () => document.body, configurable: true }); // 清空模拟函数的调用记录 jest.clearAllMocks(); });
如果用的是Mocha+Sinon这类测试框架,逻辑也是一样的:用Object.defineProperty重定义getter,用Sinon的spy来跟踪focus方法的调用就行。
内容的提问来源于stack exchange,提问作者sandrina-p
相关产品推荐
相关产品推荐

