如何Mock网站行为实现JavaScript函数单元测试(窗口场景)
刚接触JS单元测试时,碰到这种依赖浏览器环境的场景确实头疼——想测试不同窗口尺寸下的函数行为,却被真实浏览器的限制卡得死死的。我来分享几个亲测有效的方案,帮你搞定这个问题,顺便聊聊工具组合的最佳实践。
一、用Sinon正确拦截窗口尺寸的读取
你之前尝试stub $.prototype.width没效果,是因为找错了拦截的对象。如果你的函数是通过$(window).width()获取尺寸,应该直接mock $(window)这个实例的方法;如果是用原生的window.innerWidth,那就直接stub全局属性。
场景1:函数用jQuery获取窗口尺寸
假设你的函数是这样的:
function adjustBackground() { const winWidth = $(window).width(); const winHeight = $(window).height(); $('#bg').css({ width: winWidth, height: winHeight }); }
正确的测试方式是用Sinon替换$(window)的返回值,模拟width/height方法:
// 测试前准备 beforeEach(() => { // 创建一个带stub方法的模拟window对象 const mockWindow = sinon.stub({ width: () => {}, height: () => {} }); // 让$调用window时返回这个模拟对象 sinon.stub($, 'call').withArgs(window).returns(mockWindow); }); // 测试后恢复原始状态 afterEach(() => { sinon.restore(); }); it('应该将背景调整为600x1600', () => { // 设置模拟的尺寸返回值 $(window).width.returns(600); $(window).height.returns(1600); // 执行目标函数 adjustBackground(); // 断言背景尺寸正确 expect($('#bg').css('width')).to.equal('600px'); expect($('#bg').css('height')).to.equal('1600px'); });
场景2:函数用原生属性获取尺寸
如果函数直接用window.innerWidth/innerHeight,可以直接用Sinon stub全局属性:
beforeEach(() => { // 直接替换window的innerWidth和innerHeight值 sinon.stub(window, 'innerWidth').value(600); sinon.stub(window, 'innerHeight').value(1600); }); afterEach(() => { sinon.restore(); }); it('正确使用window原生尺寸', () => { adjustBackground(); // 这里写你的断言逻辑,比如检查背景元素的尺寸 });
二、触发resize事件的正确姿势
你之前触发resize没效果,是因为先触发了事件再模拟尺寸——正确顺序应该是先模拟尺寸,再触发事件:
it('窗口resize时应该更新背景尺寸', () => { // 初始尺寸 sinon.stub(window, 'innerWidth').value(800); sinon.stub(window, 'innerHeight').value(600); adjustBackground(); expect($('#bg').css('width')).to.equal('800px'); // 修改模拟的尺寸 window.innerWidth.value = 1000; window.innerHeight.value = 800; // 触发resize事件 window.dispatchEvent(new Event('resize')); // 断言尺寸已更新 expect($('#bg').css('width')).to.equal('1000px'); });
三、工具组合的最佳实践
对于原生JS项目,我最推荐的组合是:
- Mocha:测试运行器,灵活且社区生态成熟
- Chai:断言库,支持BDD/TDD风格,语法友好
- Sinon:专门处理spy/stub/mock,模拟依赖项的能力极强
如果是React、Vue这类框架项目,Jest会更省心——它内置了类似Sinon的mock功能,还集成了jsdom,不用额外配置就能模拟浏览器环境。
核心原则是:永远不要修改真实浏览器窗口,所有依赖环境的部分都用mock/stub替代,这样测试不仅更快,结果也更稳定,不会受浏览器差异影响。
四、Node环境测试:用jsdom模拟完整浏览器
如果你不想在真实浏览器里跑测试,完全可以用jsdom在Node环境模拟浏览器,直接设置窗口尺寸:
const { JSDOM } = require('jsdom'); const dom = new JSDOM('<html><body><div id="bg"></div></body></html>', { windowOptions: { innerWidth: 600, innerHeight: 1600 } }); // 把模拟的window和document挂到全局 global.window = dom.window; global.document = dom.window.document; // 之后引入jQuery和你的函数,就可以正常测试了 const $ = require('jquery')(window); const { adjustBackground } = require('./your-module'); it('在jsdom中测试背景调整', () => { adjustBackground(); expect($('#bg').css('width')).to.equal('600px'); });
这种方式完全脱离真实浏览器,适合CI/CD环境自动跑测试,也不会有iframe的CSS冲突问题。
内容的提问来源于stack exchange,提问作者Nixen85

