类型声明正常,CI环境运行Mongoose测试却报ValidationError?
Mongoose在GitHub Actions测试中出现字符串转换验证错误的问题
在GitHub Actions运行单元测试插入新的MySchema文档时,遇到如下错误:
ValidationError: MySchema validation failed: name: Cast to String failed for value "some-string" (type string) at path "name" because of "TypeError"
明明传入的是字符串,却触发类型转换失败的验证错误,且该问题仅在GitHub Actions环境出现,本地、测试及生产环境均未复现。
相关代码
Schema定义:
var MySchema = { ... name: { type: String, get: encryption.decrypt, set: encryption.encrypt }, ... }
测试工具代码:
const generateMySchema = ({ name, ...rest }) => { const name = `test_myschema_${Date.now()}`; let myTestSchema = new MySchema({ name, ...rest }); return myTestSchema; };
目前临时通过关闭CI环境的前置验证解决,但会丢失测试验证能力:
var validateBeforeSave = process.env.NODEJS_ENV !== "test-ci"; MySchema.set("validateBeforeSave", validateBeforeSave);
使用版本:Mongoose 6.10.1,MongoDB 5.0.6
问题分析与解决方法
1. 排查加密解密函数的CI环境兼容性
错误的核心是Mongoose将encryption.encrypt或encryption.decrypt执行时抛出的TypeError包装成了类型转换失败的验证错误。需要确认:
- 加密解密依赖的密钥、环境变量是否在GitHub Actions中正确注入
- 加密库在CI环境中的版本是否与本地一致(可通过锁定
package-lock.json确保依赖版本统一)
2. 给加密函数添加错误日志与捕获
在CI环境中添加日志定位问题,修改加密函数:
const encryption = { encrypt: (val) => { console.log('[CI Debug] Encrypting name:', val); try { // 原加密逻辑 const encryptedVal = /* 你的加密代码 */; console.log('[CI Debug] Encrypted result:', encryptedVal); return encryptedVal; } catch (err) { console.error('[CI Debug] Encryption failed:', err.stack); throw err; // 保留错误让Mongoose捕获,或抛出明确的验证错误 } }, decrypt: (val) => { console.log('[CI Debug] Decrypting name:', val); try { const decryptedVal = /* 你的解密代码 */; console.log('[CI Debug] Decrypted result:', decryptedVal); return decryptedVal; } catch (err) { console.error('[CI Debug] Decryption failed:', err.stack); throw err; } } };
查看GitHub Actions的日志输出,就能直接找到触发TypeError的原因。
3. 修复钩子的错误处理逻辑
避免Mongoose误判错误类型,在钩子中明确处理异常:
name: { type: String, get: (val) => { try { return encryption.decrypt(val); } catch (err) { console.error('Decryption error for name:', err); // 可选择返回默认值或抛出明确的ValidationError throw new mongoose.Error.ValidationError(null, { name: new mongoose.Error.ValidatorError({ message: `Failed to decrypt name: ${err.message}`, path: 'name', type: 'decryption_failed' }) }); } }, set: (val) => { try { return encryption.encrypt(val); } catch (err) { console.error('Encryption error for name:', err); throw new mongoose.Error.ValidationError(null, { name: new mongoose.Error.ValidatorError({ message: `Failed to encrypt name: ${err.message}`, path: 'name', type: 'encryption_failed' }) }); } } },
4. 测试环境临时替换加密逻辑(保留验证能力)
如果CI环境的加密问题暂时无法解决,可在测试中临时替换加密解密函数为原函数,既保留Mongoose的验证,又避免CI环境的加密异常:
// 在测试文件的全局前置钩子中 beforeAll(() => { // 保存原函数 global.__originalEncrypt = encryption.encrypt; global.__originalDecrypt = encryption.decrypt; // 替换为原数据返回 encryption.encrypt = (val) => val; encryption.decrypt = (val) => val; }); afterAll(() => { // 测试结束后恢复原函数 encryption.encrypt = global.__originalEncrypt; encryption.decrypt = global.__originalDecrypt; });
内容的提问来源于stack exchange,提问作者Moshisho
相关产品推荐
相关产品推荐

