如何将Node.js内部函数设为存根以进行单元测试?
如何在单元测试中隔离Handler的业务逻辑调用(Mocha + Sinon)
问题场景
现有一段Node.js Lambda处理函数,内部会根据条件调用不同的业务逻辑函数:
async function handler(event) { await parseEvent(event) if(event.a) await doBusinessLogicA(event) else await doBusinessLogicB(event) return response } async function doBusinessLogicA(event){ /* 实际业务逻辑 */ } async function doBusinessLogicB(event){ /* 实际业务逻辑 */ }
编写单元测试时,我们只需要验证handler的分支逻辑是否正确,不需要执行doBusinessLogicA或doBusinessLogicB的实际代码。但直接运行测试会触发这些业务逻辑,之前尝试过Sinon沙箱和修改module.exports设置存根,但找不到合适的恢复方式。
解决方案
核心思路是让handler内部调用的业务函数可被Sinon替换,同时确保测试后能恢复原函数,避免影响其他测试用例。
步骤1:重构模块导出
修改原模块,将业务逻辑函数也导出,同时让handler内部引用导出的版本:
async function handler(event) { await parseEvent(event) // 改为调用exports上的函数,方便后续存根替换 if(event.a) await exports.doBusinessLogicA(event) else await exports.doBusinessLogicB(event) return response } async function doBusinessLogicA(event){ /* 实际业务逻辑 */ } async function doBusinessLogicB(event){ /* 实际业务逻辑 */ } // 导出所有需要测试/存根的函数 module.exports = { handler, doBusinessLogicA, doBusinessLogicB }
步骤2:编写带存根的测试用例
使用Sinon创建业务函数的存根,在beforeEach中初始化,afterEach中恢复原函数:
const sinon = require('sinon') const myModule = require('./location-whatever') describe("Lambda tests", () => { let doBusinessLogicAStub, doBusinessLogicBStub // 每个测试前创建存根,模拟业务函数的异步返回 beforeEach(() => { doBusinessLogicAStub = sinon.stub(myModule, 'doBusinessLogicA').resolves() doBusinessLogicBStub = sinon.stub(myModule, 'doBusinessLogicB').resolves() }) // 每个测试后恢复所有存根,避免污染后续测试 afterEach(() => { sinon.restore() }) describe("Handler", () => { it("Should call doBusinessLogicA when event.a is true", async () => { await myModule.handler({a: true}) // 验证分支逻辑正确,doBusinessLogicA被调用一次 sinon.assert.calledOnce(doBusinessLogicAStub) // 验证另一个分支未触发 sinon.assert.notCalled(doBusinessLogicBStub) }) it("Should call doBusinessLogicB when event.a is false", async () => { await myModule.handler({a: false}) sinon.assert.calledOnce(doBusinessLogicBStub) sinon.assert.notCalled(doBusinessLogicAStub) }) }) })
关键说明
- 为什么要修改
handler内部调用?如果handler直接调用未导出的私有函数,Sinon无法直接替换这些函数,因为它们不在模块的导出对象上。让handler引用导出的函数,就能通过模块对象进行存根替换。 sinon.restore()会自动恢复所有由Sinon创建的存根,无需手动逐个恢复,解决了之前的恢复问题。- 用
resolves()模拟异步函数的成功返回,如果需要模拟错误,可以用rejects(new Error('test error'))。
内容的提问来源于stack exchange,提问作者Riley Shaw
相关产品推荐
相关产品推荐

