求助:使用Sinon Stub异步方法时出现解构clientId属性错误
你遇到的这个TypeError本质是你Stub的prepareInputParameters方法没有正确返回你定义的retData,导致解构时拿到了undefined。结合你说之前成功Stub过类似代码,大概率是参数匹配或者Stub时机的问题,给你几个排查和解决的方向:
1. 检查withArgs的严格匹配问题
Sinon的withArgs是严格匹配参数引用的,如果测试代码里的con/req和被测试代码中实际传入的con/req不是同一个对象引用(比如你在测试里新建了一个模拟的con,但被测试代码里用的是另一个实例),Stub就不会命中,方法会返回默认的undefined。
解决办法是用Sinon的匹配器做宽松匹配:
const retData = { clientId: 872, dateString: '1970-01-01' }; // 匹配任意对象类型的con和req sandbox.stub(parameterParser, 'prepareInputParameters') .withArgs(sinon.match.object, sinon.match.object) .resolves(retData);
如果知道con/req的特定属性,也可以精准匹配属性:
// 匹配包含dbId属性的con,和包含userId属性的req sandbox.stub(parameterParser, 'prepareInputParameters') .withArgs(sinon.match.has('dbId'), sinon.match.has('userId')) .resolves(retData);
2. 先去掉withArgs验证Stub是否生效
先暂时移除withArgs,直接Stub整个方法,看错误是否消失:
sandbox.stub(parameterParser, 'prepareInputParameters').resolves(retData);
如果错误消失了,就坐实了是参数匹配的问题,再回到第一步调整匹配规则;如果还是报错,那就要检查其他问题。
3. 确认Stub的执行时机
一定要确保Stub代码在被测试代码执行前运行。比如如果你的测试用例是先调用了被测试函数,再创建Stub,那肯定不会生效。正确的顺序应该是:
describe('Your test suite', () => { let sandbox; beforeEach(() => { sandbox = sinon.createSandbox(); // 先Stub const retData = { clientId: 872, dateString: '1970-01-01' }; sandbox.stub(parameterParser, 'prepareInputParameters').resolves(retData); }); it('should do something', async () => { // 再执行被测试代码 await yourTestedFunction(); }); });
4. 检查parameterParser的导入一致性
如果你的项目用ES模块,要确保测试文件里导入的parameterParser和被测试代码里导入的是同一个实例。有时候因为模块缓存或者第三方库的导出方式,可能会出现导入的对象不是同一个,导致Stub不生效。
如果怀疑是这个问题,可以尝试用proxyquire(针对CommonJS)强制替换实例:
// 用proxyquire替换被测试代码中的parameterParser const proxyquire = require('proxyquire'); const yourTestedModule = proxyquire('./your-tested-module', { './parameterParser': { prepareInputParameters: sandbox.stub().resolves(retData) } });
5. 明确异步方法的Stub方式
因为prepareInputParameters是异步方法(用了await),虽然Sinon的returns会自动把普通值包装成resolved的Promise,但用resolves会更清晰,也能避免一些潜在的异步处理问题:
// 用resolves代替returns,明确返回Promise sandbox.stub(parameterParser, 'prepareInputParameters') .withArgs(...) .resolves(retData);
内容的提问来源于stack exchange,提问作者Dave Chambers

