使用@jest/environment-jsdom-abstract模拟jsdom Location失败排查
处理Jest 30 + JSDOM 26中location.search模拟失败的问题
问题原因
- JSDOM 21+ 对
Location对象的属性访问做了严格限制,直接修改location.search会触发底层导航逻辑,而Jest内置的JSDOM环境默认不支持跨文档导航,因此抛出Error: Not implemented: navigation to another Document错误。 - 你尝试通过
@jest/environment-jsdom-abstract注入自定义Location实现,但Jest初始化时会优先加载内置的JSDOM环境,不会自动应用你的自定义实现,导致注入无效。
可行修改方案
方案1:用Jest Spy直接模拟getter/setter(推荐)
无需修改环境配置,直接在测试用例中对window.location.search的getter和setter做模拟,既能验证修改行为,又不会触发导航逻辑:
// 模拟search的初始值 jest.spyOn(window.location, 'search', 'get').mockReturnValue('?initial=val'); // 模拟setter,用于验证是否被修改 let mockSearchValue = ''; jest.spyOn(window.location, 'search', 'set').mockImplementation((val) => { mockSearchValue = val; }); // 执行你的业务逻辑 yourFunctionThatModifiesSearch(); // 验证修改结果 expect(mockSearchValue).toBe('?updated=value');
方案2:全局禁用JSDOM导航检查
在jest.config.js中配置JSDOM,拦截导航请求并关闭相关错误:
module.exports = { testEnvironment: 'jsdom', testEnvironmentOptions: { jsdom: { url: 'http://localhost/', pretendToBeVisual: true, // 拦截导航请求,避免触发跨文档跳转报错 onNavigation() { return Promise.resolve(null); } } } };
方案3:自定义Jest环境注入Location实现
如果需要完全自定义Location的行为,可创建自定义Jest环境:
- 创建
custom-jsdom-environment.js:
const JSDOMEnvironment = require('jest-environment-jsdom').default; class CustomJSDOMEnv extends JSDOMEnvironment { constructor(config) { super(config); // 自定义Location类,覆盖search的get/set逻辑 class CustomLocation extends this.global.Location { constructor() { super('http://localhost/'); this._search = ''; } get search() { return this._search; } set search(val) { this._search = val; // 可选:触发hashchange/popstate事件(如果业务依赖) } } // 替换全局Location类及window.location实例 this.global.Location = CustomLocation; this.global.window.location = new CustomLocation(); } } module.exports = CustomJSDOMEnv;
- 在
jest.config.js中指定自定义环境:
module.exports = { testEnvironment: './custom-jsdom-environment.js' };
注意事项
- 不要直接赋值
window.location.search或href,JSDOM 21+会触发真实导航逻辑导致报错; - 使用spy模拟时,必须明确指定
'get'或'set'选项,因为location的属性是访问器属性而非普通属性。
内容的提问来源于stack exchange,提问作者htho
相关产品推荐
相关产品推荐

