Jest测试window.crypto密码生成函数时无法生成有效值的问题
问题分析与解决方案
问题根源
你的测试代码中对window.crypto.getRandomValues的mock逻辑错误:
getRandomValues的原生行为是修改传入的TypedArray对象,填充随机值,而非返回新的数组。- 你用
mockReturnValueOnce(new Uint32Array(20))返回新数组,但原函数里的values是自己创建的空数组,mock的返回值不会被使用,导致values始终是全0的状态。 - 全0对应
charset的第一个字符'A',生成的AAAAAAAAAAAAAAAAAAAA不满足正则要求,触发无限递归。
修复后的测试代码
修改mock逻辑,让它正确修改传入的数组,同时确保生成的密码满足正则(避免递归):
it('should generate a x length password', () => { const mGetRandomValues = jest.fn((array: Uint32Array) => { // 填充数值,确保生成的密码包含大写字母、数字、特殊符号 // 索引对应charset里的字符区间:0-25=大写,52-61=数字,62+=特殊符号 array.set([ 0, // 'A'(大写) 52, // '0'(数字) 62, // '~'(特殊符号) ...Array(array.length - 3).fill(10) // 其余位置填充固定索引简化测试 ]); return array; // getRandomValues会返回传入的原数组 }); Object.defineProperty(globalThis, 'crypto', { value: { getRandomValues: mGetRandomValues }, }); const passwordLength = 20; const generatedPwd = utils.genPwd(passwordLength); // 验证密码长度 expect(generatedPwd).toHaveLength(passwordLength); // 验证getRandomValues被正确调用 expect(mGetRandomValues).toHaveBeenCalledTimes(1); const callArg = mGetRandomValues.mock.calls[0][0]; expect(callArg).toBeInstanceOf(Uint32Array); expect(callArg.length).toBe(passwordLength); // 可选:验证密码符合正则规则 expect(generatedPwd).toMatch(/^(?=.*[A-Z])(?=.*[!@#$%^&*])(?=.*[0-9]).{8,}$/); });
额外优化建议
- 避免无限递归风险:原密码生成函数如果极端情况下一直生成不满足正则的字符串,会触发栈溢出。可以添加递归次数上限:
const genPwd = (length: number, attempts = 0): string => { const maxAttempts = 10; if (attempts >= maxAttempts) { throw new Error('Failed to generate valid password after max attempts'); } // ...原有逻辑 if (!regex.test(result)) { return genPwd(length, attempts + 1); } // ... };
- 更精准的mock匹配:原测试中
toBeCalledWith(new Uint32Array(20))会失败,因为每次创建的Uint32Array是不同的对象。改用expect.any(Uint32Array)或检查实例类型+长度更可靠。
内容的提问来源于stack exchange,提问作者Pamwamba
相关产品推荐
相关产品推荐

