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

如何在Jasmine与Preact单元测试中Mock window.location

在Jasmine中Mock/Spy window.location解决单元测试失效问题

问题背景

核心函数onDeleteConfirm在调用sectionsService.deleteSections成功后,会通过window.location赋值触发页面跳转,导致单元测试过程中页面跳转,测试用例无法正常执行完成。需要在Jasmine测试中Mock或Spywindow.location,既避免实际跳转,又能验证其是否被设置为预期的URL。

核心函数代码:

function onDeleteConfirm() {
    setSuccessMessage(null);

    const sectionIds = getSelectedSectionIds().toString();
    sectionsService.deleteSections(sectionIds).then(function (result) {
        window.location = `/testing1/api/gap/export/files?fileIdentifier=${result}&storeFile=true`;
        setSections(sections.filter((s) => !s.selected));
        setSuccessMessage('Sections have been deleted successfully.');
    });
}

解决方案

方法1:Mock window.location对象

直接替换window.location为模拟对象,避免实际跳转,测试完成后恢复原始对象:

describe('onDeleteConfirm', function () {
    let wrapper, confirmDeleteModal;
    let originalLocation; // 保存原始window.location

    beforeEach(async function () {
        Enzyme.configure({ adapter: new Adapter() });
        jasmineEnzyme();

        // 保存原始location并Mock
        originalLocation = window.location;
        delete window.location;
        window.location = { href: '' };

        mockDataForSuccessRender();

        wrapper = Enzyme.mount(html`<${Index} />`);
        await testUtils.resolveImmediately();
        wrapper.update();

        const index = sections.findIndex((x) => x.sectionId === 174610);
        const sectionToUpdate = sections[index];
        sectionToUpdate.selected = true;
        sections.splice(index, 1, sectionToUpdate);
        wrapper.update();
        
        confirmDeleteModal = wrapper.find('ConfirmModal[id="confirmDeleteModal"]');
    });

    afterEach(function () {
        // 恢复原始window.location
        window.location = originalLocation;
    });

    it('should call deleteSections and set correct location on success', async function () {
        // Mock deleteSections的返回结果
        const mockResult = 'test-file-id';
        sectionsService.deleteSections.and.returnValue(Promise.resolve(mockResult));

        // 触发确认操作
        await preactTestUtils.act(function () {
            confirmDeleteModal.prop('onConfirm')();
        });

        await testUtils.resolveImmediately();
        wrapper.update();

        // 断言逻辑
        expect(sectionsService.deleteSections).toHaveBeenCalledWith('174610');
        expect(window.location.href).toBe(`/testing1/api/gap/export/files?fileIdentifier=${mockResult}&storeFile=true`);
        expect(setSuccessMessage).toHaveBeenCalledWith('Sections have been deleted successfully.');
        expect(setSections).toHaveBeenCalledWith(sections.filter(s => !s.selected));
    });
});

方法2:使用Jasmine Spy监听location赋值

通过定义属性的setter来监听window.location的赋值操作,无需替换整个对象:

describe('onDeleteConfirm', function () {
    let wrapper, confirmDeleteModal;
    let locationSpy;

    beforeEach(async function () {
        Enzyme.configure({ adapter: new Adapter() });
        jasmineEnzyme();

        // 创建Spy监听location赋值
        locationSpy = jasmine.createSpy('location');
        Object.defineProperty(window, 'location', {
            set: locationSpy,
            configurable: true
        });

        mockDataForSuccessRender();

        wrapper = Enzyme.mount(html`<${Index} />`);
        await testUtils.resolveImmediately();
        wrapper.update();

        const index = sections.findIndex((x) => x.sectionId === 174610);
        const sectionToUpdate = sections[index];
        sectionToUpdate.selected = true;
        sections.splice(index, 1, sectionToUpdate);
        wrapper.update();
        
        confirmDeleteModal = wrapper.find('ConfirmModal[id="confirmDeleteModal"]');
    });

    afterEach(function () {
        // 恢复原始location属性
        delete window.location;
    });

    it('should set correct location after delete success', async function () {
        const mockResult = 'test-file-id';
        sectionsService.deleteSections.and.returnValue(Promise.resolve(mockResult));

        await preactTestUtils.act(function () {
            confirmDeleteModal.prop('onConfirm')();
        });

        await testUtils.resolveImmediately();
        wrapper.update();

        expect(locationSpy).toHaveBeenCalledWith(`/testing1/api/gap/export/files?fileIdentifier=${mockResult}&storeFile=true`);
    });
});

关键注意点

  • 测试完成后必须恢复window.location的原始状态,避免影响其他测试用例。
  • 异步操作需等待Promise完成后再执行断言,确保所有逻辑都已执行完毕。
  • 可同时验证window.location的赋值、接口调用状态及后续的状态更新逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 01:01:10