如何在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
相关产品推荐
相关产品推荐

