如何在JavaScript单元测试中为Moment.js等外部依赖实现Stub?
测试依赖moment的包装函数与Stub的应用
如何测试verifyPasswordForCurrentDay?
因为verifyPasswordForCurrentDay直接依赖moment(),我们需要用Stub替换moment的实现,让它返回预设的星期几,以此精准控制测试场景。
用Jest实现Stub
先mock整个moment模块,再指定day()方法的返回值:
jest.mock('moment', () => { return () => ({ day: jest.fn() }); }); describe("verifyPasswordForCurrentDay", () => { test("周末调用时抛出错误", () => { const moment = require('moment'); // 让moment().day()返回周日(0) moment().day.mockReturnValue(0); expect(() => verifyPasswordForCurrentDay("any input", [])).toThrow( "It's the weekend!" ); }); test("工作日调用时不抛出错误", () => { const moment = require('moment'); // 让moment().day()返回周一(1) moment().day.mockReturnValue(1); expect(() => verifyPasswordForCurrentDay("any input", [])).not.toThrow(); }); });
用Mocha+Sinon实现Stub
通过Sinon的stub替换moment.prototype.day的返回值,测试后恢复原方法:
const sinon = require('sinon'); const moment = require('moment'); describe("verifyPasswordForCurrentDay", () => { let dayStub; beforeEach(() => { dayStub = sinon.stub(moment.prototype, 'day'); }); afterEach(() => { dayStub.restore(); }); test("周末调用时抛出错误", () => { dayStub.returns(0); // 返回周日 expect(() => verifyPasswordForCurrentDay("any input", [])).toThrow("It's the weekend!"); }); test("工作日调用时不抛出错误", () => { dayStub.returns(1); // 返回周一 expect(() => verifyPasswordForCurrentDay("any input", [])).not.toThrow(); }); });
关于“测试难题转移”的疑问
这不是转移难题,而是合理的责任拆分:
verifyPassword是核心业务逻辑函数,职责是根据输入、规则和日期验证密码。通过参数注入currentDay,让它的测试完全聚焦于业务逻辑,无需关心日期的来源,可全面覆盖周末、工作日、不同规则等场景。verifyPasswordForCurrentDay是适配层函数,职责仅为:获取当前日期,调用核心逻辑并返回结果。它的测试只需验证两点:- 正确调用
moment().day()获取日期 - 将日期正确传递给
verifyPassword并返回其结果
- 正确调用
你无需在这个函数里重复测试“周末抛错”这类业务逻辑——核心逻辑的测试已在verifyPassword中完成。拆分后,每个函数的测试更聚焦,维护成本更低:业务逻辑变更时只需修改verifyPassword的测试;日期获取方式变更时(比如改用原生Date),只需调整verifyPasswordForCurrentDay的测试,两者互不影响。
内容的提问来源于stack exchange,提问作者Behnam
相关产品推荐
相关产品推荐

