如何在Backbone Marionette单元测试中Mock $window解决页面重载问题?
解决单元测试中window.location.href触发页面重载的问题
嘿,我来帮你搞定这个单元测试里页面跳转导致失败的问题!核心思路就是模拟(Mock)全局的window对象,让测试时的跳转操作不会真的刷新页面,同时还能验证这个跳转行为是否正确执行。
一、用Jasmine实现Mock(最常用的前端测试框架)
假设你用的是Jasmine,我们可以在测试套件里先保存原始window,测试前替换成模拟对象,测试后再恢复,避免影响其他测试用例:
describe('showEnrollRoute 函数测试', () => { let originalWindow; let mockWindow; // 每个测试前初始化Mock beforeEach(() => { // 保存真实的window对象 originalWindow = window; // 创建Mock的window,包含location属性用于模拟跳转 mockWindow = { location: { href: '' } }; // 替换全局window为Mock对象(浏览器环境直接用window,Node环境用global.window) global.window = mockWindow; }); // 测试结束后恢复原始window afterEach(() => { global.window = originalWindow; }); // 测试无用户名cookie时的跳转行为 it('当未找到username cookie时,应跳转到指定的logoutSuccess页面', () => { // 先Mock getCookie方法,让它返回null(模拟无cookie的情况) spyOn(Application.utils, 'getCookie').and.returnValue(null); // 调用要测试的函数 yourModule.showEnrollRoute(); // 替换成你实际调用该函数的方式 // 验证是否正确设置了跳转地址 expect(mockWindow.location.href).toBe(Application.getConfig("logoutSuccess")); // 可选:验证console.log是否按预期执行 expect(console.log).toHaveBeenCalledWith("Start"); expect(console.log).toHaveBeenCalledWith("End"); }); // 测试有用户名cookie时的逻辑 it('当存在username cookie时,不应触发跳转', () => { // Mock getCookie返回有效用户名 spyOn(Application.utils, 'getCookie').and.returnValue('test-user'); // Mock channel相关逻辑,避免实际发送请求 const mockChannel = { request: jasmine.createSpy('request') }; spyOn(module, 'getChannel').and.returnValue(Promise.resolve(mockChannel)); // 调用函数 yourModule.showEnrollRoute(); // 验证跳转地址未被修改 expect(mockWindow.location.href).toBe(''); // 验证频道请求是否触发 expect(mockChannel.request).toHaveBeenCalledWith("fetQuestions"); }); });
二、优化建议:依赖注入window(更优雅的方式)
如果能修改原函数,最好把window作为依赖传入,这样Mock起来更安全,不用修改全局对象:
// 修改原函数,添加可选的window参数(默认用全局window) showEnrollRoute: function(windowObj = window){ var username = Application.utils.getCookie("username"); // 注意:原代码的条件判断有bug!应该用&&而不是||,否则永远为true if(username != null && username != undefined){ Application.vent.trigger("refresh:header"); var channel = module.getChannel("counties"); channel.then(function(channel){ channel.request("fetQuestions"); }); } else { console.log("Start"); windowObj.location.href= Application.getConfig("logoutSuccess"); console.log("End"); } }
测试时直接传入Mock的window对象即可:
it('测试跳转逻辑', () => { const mockWindow = { location: { href: '' } }; spyOn(Application.utils, 'getCookie').and.returnValue(null); yourModule.showEnrollRoute(mockWindow); expect(mockWindow.location.href).toBe(Application.getConfig("logoutSuccess")); });
三、Mocha + Sinon的适配方案
如果你用的是Mocha搭配Sinon,同样可以用Sinon来存根window.location.href:
const sinon = require('sinon'); describe('showEnrollRoute', () => { let locationStub; beforeEach(() => { // 存根location.href的setter和getter locationStub = sinon.stub(window.location, 'href').set(function(val) { this._href = val; }); locationStub.get(function() { return this._href || ''; }); }); afterEach(() => { // 恢复原始的location.href locationStub.restore(); }); it('无用户名时触发跳转', () => { sinon.stub(Application.utils, 'getCookie').returns(null); yourModule.showEnrollRoute(); expect(locationStub.calledWith(Application.getConfig("logoutSuccess"))).toBe(true); }); });
最后提醒下:原函数里的if(username != null || username != undefined)这个条件是错的,不管username是null还是undefined,这个条件都会为true,应该改成&&或者更简洁的if(username)哦!
内容的提问来源于stack exchange,提问作者swathi
相关产品推荐
相关产品推荐

