You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    });
});

关键说明

  1. window.location是浏览器特殊只读对象,无法直接赋值替换,只能通过修改属性或模拟getter实现测试场景。
  2. 必须用jest.mock来mockredirect函数,确保测试中调用的是mock实例而非真实实现。
  3. 用jest.MockedFunction为mock函数添加类型,避免TypeScript类型错误。

内容的提问来源于stack exchange,提问作者noblerare

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 11:25:27