Jest+JSDOM异常:dom.window.document.getElementById成功但全局document.getElementById失效
解决JSDOM+Jest中global.document.getElementById返回null的问题
问题现象
- 设置1000ms延迟,排除时序问题影响
document.documentElement.outerHTML能正常显示完整HTML内容,说明document可访问DOM结构document是有效的Document对象,document.getElementById为有效函数document.getElementById('loginEmailForm')莫名返回null- 调用
dom.window.document.getElementById('loginEmailForm')能正确获取到HTMLElement{},无论在前者调用前后结果一致 window.document.getElementById('loginEmailForm')与document.getElementById('loginEmailForm')始终返回null
环境版本
- node v22.3.0
- jest v29.7.0
- jsdom v24.1.0
测试代码
const { JSDOM } = require('jsdom'); beforeEach(() => { jest.resetModules(); const dom = new JSDOM(` <!DOCTYPE html> <html lang="en"> <head> <title>test</title> </head> <body> <form id="loginEmailForm"> <input type="email" id="email"> <span id="emailError"></span> <button type="submit">Submit</button> </form> </body> </html> `, { runScripts: "dangerously" }); global.dom = dom; global.window = dom.window; global.document = dom.window.document; require('../src/login-email.js'); }); describe('Login Email Form', () => { test('form and input elements exist', async () => { await new Promise(resolve => setTimeout(resolve, 1000)); console.log("document:", document.documentElement.outerHTML); console.log(document); console.log(document.getElementById); console.log(document.getElementById('loginEmailForm')); console.log(dom.window.document.getElementById('loginEmailForm')); expect(dom.window.document.getElementById('loginEmailForm')).not.toBeNull(); expect(dom.window.document.getElementById('email')).not.toBeNull(); expect(dom.window.document.getElementById('emailError')).not.toBeNull(); }); });
问题原因
核心问题是你引入的../src/login-email.js脚本中,可能存在修改全局document或window.document引用的操作,导致挂载到global的document不再指向JSDOM创建的原始Document对象。
另外,runScripts: "dangerously"选项会允许执行HTML及外部脚本中的代码,若脚本内有覆盖全局document的逻辑,就会出现全局document与dom.window.document分离的情况。
解决方案
1. 排查并修复login-email.js中的document操作
检查login-email.js文件,确认是否存在直接赋值document的代码(如document = ...),或使用了会修改全局document引用的库。确保脚本内的document操作始终指向JSDOM创建的实例,避免重新赋值。
2. 调整测试代码,避免全局document挂载冲突
修改测试逻辑,不再直接将document挂载到global,而是通过dom.window直接获取document引用,确保上下文一致:
const { JSDOM } = require('jsdom'); beforeEach(() => { jest.resetModules(); const dom = new JSDOM(` <!DOCTYPE html> <html lang="en"> <head> <title>test</title> </head> <body> <form id="loginEmailForm"> <input type="email" id="email"> <span id="emailError"></span> <button type="submit">Submit</button> </form> </body> </html> `, { runScripts: "dangerously" }); global.dom = dom; global.window = dom.window; // 移除全局document挂载,让脚本通过window访问document require('../src/login-email.js'); }); describe('Login Email Form', () => { test('form and input elements exist', async () => { await new Promise(resolve => setTimeout(resolve, 1000)); const doc = dom.window.document; console.log("document:", doc.documentElement.outerHTML); console.log(doc.getElementById('loginEmailForm')); expect(doc.getElementById('loginEmailForm')).not.toBeNull(); expect(doc.getElementById('email')).not.toBeNull(); expect(doc.getElementById('emailError')).not.toBeNull(); }); });
3. 禁用不必要的runScripts选项
如果测试不需要执行HTML或外部脚本中的JS代码,可移除runScripts: "dangerously"选项,避免脚本修改DOM上下文:
const dom = new JSDOM(`...`); // 不设置runScripts选项
内容的提问来源于stack exchange,提问作者user3642765
相关产品推荐
相关产品推荐

