Jest重复Mock navigator测试userAgent/vendor时的缓存问题排查
测试意图
- 循环使用不同的userAgent组合开展测试
- Mock navigator对象模拟不同设备环境
- 验证目标函数输出是否符合预期
当前问题
- 首次Mock navigator.userAgent成功,测试用例执行结果符合预期
- 第二次执行Mock后,测试仍读取第一次Mock的userAgent值,导致结果异常
待测试函数
export const isBrowseriOS = () => { console.log(navigator.userAgent, ' & ', navigator.vendor); return ( navigator.vendor === 'Apple Computer, Inc.' || (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream && 'ontouchend' in document) ); };
测试文件代码
import { isBrowseriOS } from './isIOS'; const browserDevices = [ { name: 'iPhone 12 Pro', userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', vendor: 'Apple Computer, Inc.', iOSDevice: true, }, { name: 'Samsung SM-G955U', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36', vendor: 'Google Inc.', iOSDevice: false, }, ]; describe.each(browserDevices)('iOS test for $name', (device) => { beforeEach(() => { Object.defineProperty(window, 'navigator', { value: { userAgent: device.userAgent, vendor: device.vendor, }, }); }); afterEach(() => jest.resetAllMocks()); it(`returns ${device.iOSDevice}`, () => { expect(isBrowseriOS()).toBe(device.iOSDevice); }); });
测试结果
测试结果异常,第二个测试用例未读取到正确的Mock值,仍然沿用第一次的userAgent数据。
解决方案
问题根源在于Object.defineProperty默认配置下,window.navigator属性不可修改,首次定义后后续无法重新赋值。需要添加configurable: true允许属性被重新定义,同时在测试后恢复原始navigator对象,避免测试环境污染。
修改后的测试代码如下:
import { isBrowseriOS } from './isIOS'; const browserDevices = [ { name: 'iPhone 12 Pro', userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', vendor: 'Apple Computer, Inc.', iOSDevice: true, }, { name: 'Samsung SM-G955U', userAgent: 'Mozilla/5.0 (Linux; Android 8.0.0; SM-G955U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Mobile Safari/537.36', vendor: 'Google Inc.', iOSDevice: false, }, ]; // 保存原始navigator对象,用于测试后恢复 const originalNavigator = window.navigator; describe.each(browserDevices)('iOS test for $name', (device) => { beforeEach(() => { Object.defineProperty(window, 'navigator', { value: { userAgent: device.userAgent, vendor: device.vendor, }, configurable: true, // 允许重新定义该属性 }); }); afterEach(() => { // 恢复原始navigator,清除测试环境修改 delete window.navigator; Object.defineProperty(window, 'navigator', { value: originalNavigator, configurable: true, }); }); it(`returns ${device.iOSDevice}`, () => { expect(isBrowseriOS()).toBe(device.iOSDevice); }); });
关键说明
configurable: true:解除属性不可修改的限制,让每次测试都能重新定义navigator- 保存并恢复原始
navigator:确保测试完成后环境回到初始状态,避免影响其他测试用例 - 替换
jest.resetAllMocks():该方法无法重置通过Object.defineProperty添加的属性,必须手动恢复环境
内容的提问来源于stack exchange,提问作者Akber Iqbal
相关产品推荐
相关产品推荐

