Jest中如何为浏览器函数正确传入Mock DOM?
解决Jest中Mock DOM无效导致
getElementById返回null的问题 问题根源
你的测试代码存在两个核心问题:
- 模块加载时机错误:先引入了
tested_file.js,之后才挂载自定义的document到global。此时被测函数里的document已经绑定了Jest初始的空DOM环境,后续替换global.document不会影响已加载模块里的引用。 - 重复创建DOM环境:你已经在
package.json中配置了testEnvironment: "jsdom",Jest会自动提供完整的浏览器DOM环境,手动引入jsdom包并创建实例会和内置环境冲突。
修复方案
1. 精简依赖配置
修改package.json,移除手动安装的jsdom包(jest-environment-jsdom已包含所需的jsdom功能),清理冗余配置:
{ "scripts": { "js_test": "jest" }, "jest": { "testEnvironment": "jsdom" }, "devDependencies": { "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "text-encoding": "^0.7.0" } }
执行命令移除多余依赖:
npm uninstall jsdom
2. 调整测试代码逻辑
把DOM初始化逻辑放到被测模块引入之前,或者在测试用例/前置钩子中动态设置DOM结构,确保被测函数执行时能获取到正确的DOM:
const { TextEncoder, TextDecoder } = require('text-encoding'); global.TextEncoder = TextEncoder; global.TextDecoder = TextDecoder; // 先初始化DOM结构 document.body.innerHTML = ` <img id="id1" src="foo.jpg" /> `; // 再引入被测函数 const { hello_world } = require("<path>/tested_file.js"); describe('hello_world', () => { test('returns correct id', () => { const x = hello_world(); expect(x).toBe("id1"); }); });
如果是多测试用例场景,推荐用beforeEach钩子在每个测试前重置DOM:
const { TextEncoder, TextDecoder } = require('text-encoding'); global.TextEncoder = TextEncoder; global.TextDecoder = TextDecoder; const { hello_world } = require("<path>/tested_file.js"); describe('hello_world', () => { beforeEach(() => { // 每次测试前重置DOM结构 document.body.innerHTML = ` <img id="id1" src="foo.jpg" /> `; }); test('returns correct id', () => { const x = hello_world(); expect(x).toBe("id1"); }); });
原理说明
当Jest使用jsdom环境时,会自动在global上挂载window、document等浏览器全局对象。只要在调用被测函数前设置好DOM结构,函数就能正确访问到对应的DOM元素。模块加载时会静态绑定当前的document引用,因此必须确保DOM初始化完成后再引入被测模块,或者在每次测试前重置DOM状态。
内容的提问来源于stack exchange,提问作者ArchipelagoT
相关产品推荐
相关产品推荐

