Sinon Stub触发意外UnhandledPromiseRejectionWarning且测试不通过
咱们先拆解一下你遇到的两个问题,其实它们是相关的,核心原因是异步函数的错误处理和断言方式不对。
为什么会出现UnhandledPromiseRejectionWarning?
当你用sandbox.stub(sqlFileReader, 'read').rejects(...)模拟Promise拒绝时,你的测试代码里如果没有正确捕获这个拒绝,Node.js就会认为这是一个未处理的Promise拒绝,从而抛出警告。你之前用expect(async () => {...}).to.throw()的写法,本质上没有正确处理这个Promise的拒绝状态,导致警告出现。
为什么测试断言失败?
普通的to.throw()断言只能捕捉同步函数抛出的错误,而你的performDatabaseConnectivityHealthCheck是异步函数,它的错误是通过Promise拒绝传递的,不是同步抛出的。所以to.throw()根本捕捉不到这个错误,自然断言失败。
解决方案:使用chai-as-promised处理异步断言
要同时解决这两个问题,我们需要用专门处理Promise的断言工具——chai-as-promised,它能完美处理异步函数的错误断言,同时自动处理Promise拒绝,消除警告。
步骤1:安装并引入chai-as-promised
如果还没安装,先执行:
npm install chai-as-promised --save-dev
然后在你的测试文件顶部添加:
const chai = require('chai'); const chaiAsPromised = require('chai-as-promised'); // 启用chai-as-promised插件 chai.use(chaiAsPromised);
步骤2:修改测试用例的断言逻辑
把原来的同步断言写法改成异步Promise断言:
it('throws a \'Database Healthcheck Error\' error when the Database Healthcheck gives the wrong data', async () => { // 用sinon的.rejects()模拟异步函数抛出错误,这是最简洁正确的写法 sandbox.stub(sqlFileReader, 'read').rejects(new Error("ER_PROCACCESS_DENIED_ERROR: execute command denied to user 'dchambers'@'%' for routine 'uk_txtloan.connection_ids'")); // 直接断言Promise会被拒绝,并用rejectedWith匹配错误信息 await expect(RiskAffordabilityReportService.performDatabaseConnectivityHealthCheck(rars.logger)) .to.be.rejectedWith("ER_PROCACCESS_DENIED_ERROR: execute command denied to user 'dchambers'@'%' for routine 'uk_txtloan.connection_ids'"); });
额外说明:关于sinon stub的写法
你尝试的几种写法里,.rejects(...)是sinon专门为异步函数设计的正确写法,它会自动返回一个被拒绝的Promise,比.returns(Promise.reject(...))更简洁可靠。之前的.throws(...)是用来模拟同步函数抛出错误的,不适合异步场景,所以无效。
这样修改后,测试会正确捕获异步函数的Promise拒绝,既不会出现UnhandledPromiseRejectionWarning,也能让断言通过。
内容的提问来源于stack exchange,提问作者Dave Chambers

