Jest 22升级后window.location单测Mock失效,咨询多URL配置方案
我之前也踩过Jest 22这个坑!原来用Object.defineProperty修改window.location.href的路子确实走不通了——Jest从22版本开始,为了更贴近真实浏览器的行为,把window.location设成了不可配置、不可直接改写的属性,直接用defineProperty会被忽略或者报错。不过别担心,有几个靠谱的方法能实现每个测试文件单独指定URL:
方法1:利用jsdom的reconfigure API(推荐)
Jest底层依赖jsdom模拟浏览器环境,我们可以直接调用jsdom的API来修改当前测试环境的URL,这是最贴近原生行为的方式:
// 在测试文件的开头或者beforeAll钩子中执行 beforeAll(() => { // 获取当前jsdom的window实例 const currentWindow = global.document.defaultView; // 重新配置URL currentWindow.reconfigure({ url: 'https://example.com/your-specific-path' }); });
这个方法会完整更新整个window.location对象的所有属性(比如hostname、pathname、search等),和真实浏览器的URL变化一致,不会有属性缺失的问题。
方法2:手动替换window.location为URL对象
如果你的测试只需要用到href等基础属性,可以直接把window.location替换成一个URL实例,测试结束后再恢复原对象:
let originalLocation; beforeAll(() => { // 保存原生location,方便测试后恢复 originalLocation = window.location; // 删除原生location(因为它不可配置,先删再替换) delete window.location; // 替换成自定义URL的实例 window.location = new URL('https://example.com/another-path'); }); afterAll(() => { // 测试完成后恢复原生location,避免影响其他测试 window.location = originalLocation; });
这种方式简单直接,适合只需要模拟href、pathname等常用属性的场景。
方法3:使用Jest的模块重置+动态设置(适合复杂场景)
如果你的测试代码是模块化导入的,可能需要先重置模块再设置URL,确保模块加载时能读取到新的URL:
beforeAll(() => { const currentWindow = global.document.defaultView; currentWindow.reconfigure({ url: 'https://example.com/module-specific-url' }); // 重置所有已导入的模块,确保重新加载时使用新的URL jest.resetModules(); }); // 然后在这里导入你的业务模块 const yourModule = require('../your-module');
这个方法适合那些在模块初始化阶段就读取window.location的代码,重置模块后重新导入,就能让模块获取到新的URL了。
补充说明
为什么原来的方法失效?Jest 22+对window.location的模拟做了调整,把它设置成了和真实浏览器一样的不可配置属性,所以Object.defineProperty无法修改它的可写性,自然也就不能直接覆盖href值了。上面的几种方法都是绕开这个限制,直接修改jsdom的环境或者替换整个location对象。
内容的提问来源于stack exchange,提问作者Natsathorn

