使用函数解构调用index模块触发TypeError: callback未定义
看起来你遇到的TypeError: callback is not a function错误,根源在于测试时缺少必要参数导致函数调用的参数顺序错乱,加上async.waterfall的使用存在一些小疏漏,具体分析和解决步骤如下:
1. 核心原因:参数缺失导致回调位置被undefined占据
你的导出函数接收解构后的对象参数(redshiftDetails、sqs、lambda、redshiftQueryParams),但测试时只传入了redshiftDetails,其他参数(比如lambda)默认是undefined。
假设在getRedshiftCredentials函数内部,你调用getTemporaryRedshiftCredentials的代码类似这样:
helpers.getTemporaryRedshiftCredentials(redshiftDetails, lambda, callback);
由于测试时lambda是undefined,如果getTemporaryRedshiftCredentials的定义只需要两个参数(redshiftDetails和回调函数),那么第二个参数就变成了undefined(本该是回调的位置被undefined占据)。当Sinon的stub尝试调用这个“回调”时,自然会抛出callback is not a function错误。
2. 解决方案步骤
步骤1:给解构参数设置默认值,避免undefined
修改导出函数的参数定义,给可选参数设置空对象默认值,确保即使测试时只传部分参数,也不会出现undefined打乱参数顺序:
module.exports = function ({ redshiftDetails, sqs = {}, lambda = {}, redshiftQueryParams = {} }, callback) { // 原有逻辑... }
步骤2:区分回调变量名,避免参数混淆
把async.waterfall步骤里的回调和外层的回调用不同变量名区分,防止变量名冲突导致的意外问题,同时确保调用工具函数时回调始终是最后一个参数:
function getRedshiftCredentials (sqsMessagesList, waterfallCallback) { logger.info('Obtaining redshift credentials...'); // 明确传递正确的回调参数,避免被缺失的参数挤占位置 helpers.getTemporaryRedshiftCredentials(redshiftDetails, (err, credentials) => { if (err) { return waterfallCallback(err); // 把错误传递给waterfall的下一步 } // 后续业务逻辑... waterfallCallback(null, credentials); }); }
步骤3:给async.waterfall添加最终回调
你的代码里async.waterfall只定义了步骤数组,没有传递最终的回调函数,这会导致整个流程结束后无法触发外层的callback,测试里的done()可能永远不会被调用。补充最终回调:
async.waterfall([ async.apply(helpers.bulkReceiveFromSQS, sqs), function getRedshiftCredentials (sqsMessagesList, waterfallCallback) { // 原有逻辑... }, // 其他业务步骤... ], function (finalErr, finalResult) { // 把最终结果或错误传递给外层的callback if (finalErr) { logger.error('Ingestion workflow failed', finalErr); return callback(finalErr); } callback(null, metrics); // 传递最终结果 });
步骤4:测试时补充必要模拟参数(可选)
如果某些参数是工具函数必须的(比如sqs在bulkReceiveFromSQS里是必填项),测试时可以传入空的模拟对象,避免参数缺失引发其他问题:
it('should fail if the fetching temporary credentials of redshift fails', function (done) { stubbedGetTemporaryRedshiftCredentials.yields(new Error('call to getTemporaryRedshiftCredentials failed')); stubbedBulkReceiveFromSQS.yields(null); // 传入模拟的sqs和lambda对象 ingestionController({ redshiftDetails: {clusterId: 'someid', iamRole: 'somerole'}, sqs: {}, lambda: {} }, function (err) { expect(err).to.be.an('error'); expect(err.message).to.be.equal('call to getTemporaryRedshiftCredentials failed'); expect(stubbedRedshiftClose.callCount).to.be.equal(0); done(); }); });
3. 验证修复
完成上述修改后,重新运行测试,TypeError应该会消失,测试逻辑也能正常触发外层的callback,调用done()结束测试。
内容的提问来源于stack exchange,提问作者Sayed Awesh Rahman

