TypeScript Jest测试中模拟window.location的TS错误解决
修复TypeScript测试中window.location模拟的问题
原业务代码
import { redirect } from './some-utils' export const goToURL = () => { const url = window.location.origin + window.location.pathname redirect(url) }
测试代码(存在TypeScript错误)
describe('my-test-file', () => { let originalWindowLocation const redirect = jest.fn() beforeEach(() => { jest.resetAllMocks() originalWindowLocation = window.location }) afterEach(() => { window.location = originalWindowLocation }) it('test that redirection URL is correct', () => { delete window.location // TypeScript报错 window.location = { origin: 'https://www.example.com', pathname: '/mypath' } // TypeScript报错 goToURL() expect(redirect).toHaveBeenCalledTimes(1) expect(redirect).toHaveBeenCalledWith('https://www.example.com/mypath') }) })
遇到的TypeScript错误
delete语句错误:
The operand of a 'delete' operator must be optional.
(delete操作符的操作数必须是可选的)window.location赋值错误:
Type '{ origin: string; pathname: string; }' is not assignable to type 'Location | (string & Location)'. Type '{ origin: string; pathname: string; }' is not assignable to type 'string & Location'.
Type '{ origin: string; pathname: string; }' is not assignable to type 'string'.
(类型“{ origin: string; pathname: string; }”无法分配给类型“Location | (string & Location)”)
尝试过的无效方案
- 移除delete语句并添加
as Location类型断言,测试未通过,代码仍使用真实域名而非测试域名 - 尝试直接赋值
window.location = 目标URL、修改window.location.href、调用window.location.assign(目标URL),均未解决问题
解决方案
方法1:用Object.defineProperty模拟属性
window.location是浏览器只读对象,无法直接替换,需单独模拟其属性:
describe('my-test-file', () => { let originalOrigin: string; let originalPathname: string; const redirect = jest.fn() as jest.MockedFunction<typeof import('./some-utils').redirect>; // Mock redirect模块 jest.mock('./some-utils', () => ({ redirect: jest.fn() })); beforeEach(() => { jest.resetAllMocks(); // 保存原始属性值 originalOrigin = window.location.origin; originalPathname = window.location.pathname; }); afterEach(() => { // 恢复原始属性 Object.defineProperty(window.location, 'origin', { value: originalOrigin, writable: true }); Object.defineProperty(window.location, 'pathname', { value: originalPathname, writable: true }); }); it('test that redirection URL is correct', () => { // 模拟测试用属性 Object.defineProperty(window.location, 'origin', { value: 'https://www.example.com', writable: true }); Object.defineProperty(window.location, 'pathname', { value: '/mypath', writable: true }); goToURL(); expect(redirect).toHaveBeenCalledTimes(1); expect(redirect).toHaveBeenCalledWith('https://www.example.com/mypath'); }); });
方法2:用jest.spyOn模拟属性getter
利用jest的spyOn功能替换属性的取值逻辑:
describe('my-test-file', () => { const redirect = jest.fn() as jest.MockedFunction<typeof import('./some-utils').redirect>; // Mock redirect模块 jest.mock('./some-utils', () => ({ redirect: jest.fn() })); beforeEach(() => { jest.resetAllMocks(); }); it('test that redirection URL is correct', () => { // 模拟origin和pathname的getter const originSpy = jest.spyOn(window.location, 'origin', 'get').mockReturnValue('https://www.example.com'); const pathnameSpy = jest.spyOn(window.location, 'pathname', 'get').mockReturnValue('/mypath'); goToURL(); expect(redirect).toHaveBeenCalledTimes(1); expect(redirect).toHaveBeenCalledWith('https://www.example.com/mypath'); // 清理spy originSpy.mockRestore(); pathnameSpy.mockRestore(); }); });
关键说明
window.location是浏览器特殊只读对象,无法直接赋值替换,只能通过修改属性或模拟getter实现测试场景。- 必须用
jest.mock来mockredirect函数,确保测试中调用的是mock实例而非真实实现。 - 用
jest.MockedFunction为mock函数添加类型,避免TypeScript类型错误。
内容的提问来源于stack exchange,提问作者noblerare
相关产品推荐
相关产品推荐

