NodeJS Promise拒绝时JSON缺失key的单元测试问题排查
我一眼就看出问题所在了——chai-as-promised的assert.isRejected默认期望被拒绝的是一个Error实例,而不是普通的JavaScript对象。
你现在的代码里,reject的是一个包含code和message的普通对象,但chai-as-promised在处理这种情况时,会自动把非Error类型的拒绝值转换成字符串(通常是取message字段,或者直接调用toString()),所以你只能看到message,code字段自然就丢失了,这就是断言失败的核心原因。
下面给你两种可行的解决思路:
方案1:让函数reject一个带自定义属性的Error实例(推荐)
这是更符合JavaScript错误处理规范的做法,毕竟Promise的reject通常用来传递错误信息,用Error实例能保留完整的调用栈等信息。
修改你的函数代码:
exports.validate = (strId) => { return new Promise((resolve, reject) => { if (!strId) { const validationError = new Error('Required header is not present in request'); // 把自定义code挂载到Error实例上 validationError.code = constants.ID_NOT_VALID; return reject(validationError); // 其余逻辑 } }); }
然后调整测试代码,用rejectedWith结合属性断言,这样既能验证是Error类型,又能检查自定义的code和message:
it('should not be empty', async () => { await expect(validate.validateCorrelationId()) .to.be.rejectedWith(Error) .and.have.property('code', constants.ID_NOT_VALID) .and.have.property('message', 'Required header is not present in request'); });
如果坚持用assert.isRejected,可以链式调用then来检查错误属性:
it('should not be empty', async () => { return assert.isRejected(validate.validateCorrelationId(), Error) .then(err => { assert.equal(err.code, constants.ID_NOT_VALID); assert.equal(err.message, 'Required header is not present in request'); }); });
方案2:不修改原函数,直接断言被拒绝的普通对象
如果你不想改动现有函数的返回逻辑,可以直接捕获被拒绝的值,然后用深度相等断言来匹配整个对象:
it('should not be empty', async () => { // 直接捕获reject的值 const rejectedErr = await validate.validateCorrelationId().catch(err => err); // 用deepEqual匹配整个对象 assert.deepEqual(rejectedErr, { code: constants.ID_NOT_VALID, message: 'Required header is not present in request' }); });
或者用chai-as-promised的rejected结合deep.equal:
it('should not be empty', async () => { await expect(validate.validateCorrelationId()) .to.be.rejected .and.deep.equal({ code: constants.ID_NOT_VALID, message: 'Required header is not present in request' }); });
为什么你原来的测试会失败?
你之前用assert.isRejected(promise, errJson),chai-as-promised会把第二个参数当成Error的匹配条件(比如匹配message或者name),而不是直接匹配整个拒绝值。当拒绝值是普通对象时,它会尝试把对象转换成字符串来匹配,所以只会提取message字段,导致断言时找不到code,最终失败。
内容的提问来源于stack exchange,提问作者levo4ka

