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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:26:41