jest-when高级技巧:函数匹配器与allArgs实现复杂参数校验
jest-when高级技巧:函数匹配器与allArgs实现复杂参数校验
【免费下载链接】jest-whenJest support for mock argument-matched return values.项目地址: https://gitcode.com/gh_mirrors/je/jest-when
想要在Jest测试中精确控制模拟函数的返回值吗?jest-when提供了强大的函数匹配器和allArgs功能,让复杂参数校验变得简单直观!🚀
jest-when是Jest的一个强大扩展库,专门用于基于参数匹配来训练模拟函数的行为。不同于传统的Jest模拟只能返回固定值,jest-when允许你根据不同的参数组合返回不同的值,让测试更加精确和灵活。本文将深入探讨jest-when的高级功能——函数匹配器和allArgs方法,帮助你处理复杂的参数校验场景。
🤔 为什么需要函数匹配器?
在单元测试中,我们经常遇到这样的场景:需要根据参数的特定条件来返回不同的结果。传统的Jest模拟虽然支持mockImplementation,但代码会变得冗长且难以维护:
// 传统方式 - 代码冗长 const fn = jest.fn((arg1, arg2) => { if (arg1 > 10 && arg2 === 'active') { return 'success'; } else if (typeof arg1 === 'string' && arg2.includes('test')) { return 'partial'; } return 'default'; });jest-when的函数匹配器让这一切变得优雅:
import { when } from 'jest-when'; const isGreaterThan10 = when((n: number) => n > 10); const isActive = when((status: string) => status === 'active'); const containsTest = when((str: string) => str.includes('test')); when(fn).calledWith(isGreaterThan10, isActive).mockReturnValue('success'); when(fn).calledWith(expect.any(String), containsTest).mockReturnValue('partial'); when(fn).defaultReturnValue('default');🔧 创建自定义函数匹配器
函数匹配器的核心在于将普通的判断函数转换为jest-when能够识别的匹配器。你可以在src/when.ts中看到其实现原理:
// 创建函数匹配器的示例 const isEven = when((n: number) => n % 2 === 0); const isValidEmail = when((email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)); const hasAdminRole = when((user: User) => user.roles.includes('admin')); // 使用函数匹配器 when(userService.getUser) .calledWith(isValidEmail) .mockResolvedValue({ id: 1, name: 'Valid User' }); when(authService.checkPermission) .calledWith(hasAdminRole, expect.any(String)) .mockReturnValue(true);实际应用场景
场景1:验证复杂对象结构
const isValidProduct = when((product: any) => product?.id && product?.name && product?.price > 0 && product?.category !== undefined ); when(productService.createProduct) .calledWith(isValidProduct) .mockResolvedValue({ success: true, id: '123' });场景2:组合多个条件
const isPremiumUser = when((user: User) => user.subscription === 'premium' && user.active === true ); const hasValidToken = when((token: string) => token && token.length > 10 && token.startsWith('Bearer_') ); when(authService.authenticate) .calledWith(isPremiumUser, hasValidToken) .mockResolvedValue({ access: 'full', expiresIn: 3600 });🎯 allArgs:全参数匹配的终极武器
当单个参数匹配器不够用时,when.allArgs提供了更强大的解决方案。它允许你一次性检查所有参数,实现复杂的跨参数逻辑校验。
基础用法
查看src/when.ts中的allArgs实现:
// 检查所有参数都是数字 const allNumbers = when.allArgs((args: any[]) => args.every(arg => typeof arg === 'number') ); when(calculator.sum) .calledWith(allNumbers) .mockReturnValue('valid calculation'); // 测试 calculator.sum(1, 2, 3); // 返回 'valid calculation' calculator.sum(1, 'two', 3); // 返回 undefined(不匹配)高级模式:参数索引匹配
// 创建一个匹配特定索引参数的辅助函数 const argAtIndex = (index: number, matcher: any) => when.allArgs((args: any[], equals) => equals(args[index], matcher)); when(api.call) .calledWith(argAtIndex(0, expect.any(Number))) .calledWith(argAtIndex(1, 'required')) .mockReturnValue('valid call'); api.call(123, 'required', 'extra'); // 匹配 api.call('string', 'required'); // 不匹配实际案例:React组件Props匹配
在src/when.test.ts中有一个React用例:
// React组件props匹配 const SomeChild = jest.fn(); const propsOf = (propsToMatch: any) => when.allArgs(([props, refOrContext]: any[], equals) => equals(props, propsToMatch) ); when(SomeChild) .calledWith(propsOf({ xyz: '123' })) .mockReturnValue('hello world'); SomeChild({ xyz: '123' }); // 返回 'hello world'⚠️ 重要注意事项
1. allArgs的独占性
when.allArgs必须是calledWith的唯一匹配器参数。混合使用会抛出错误:
// ❌ 错误用法 when(fn).calledWith(when.allArgs(() => true), 1, 2, 3); // 抛出错误 // ✅ 正确用法 when(fn).calledWith(when.allArgs(() => true)); // 正确2. 函数匹配器的错误处理
使用expectCalledWith时,函数匹配器失败会提供详细的错误信息:
const isPositive = when((n: number) => n > 0); when(fn).expectCalledWith(isPositive).mockReturnValue('positive'); fn(-5); // 抛出错误:Failed function matcher within expectCalledWith...3. 性能考虑
对于简单的参数匹配,优先使用Jest的内置匹配器(如expect.any()、expect.objectContaining())。函数匹配器和allArgs更适合复杂逻辑。
🚀 最佳实践组合
组合1:验证API请求
const isValidRequest = when.allArgs(([method, url, data, headers]: any[]) => ['GET', 'POST', 'PUT', 'DELETE'].includes(method) && url.startsWith('/api/') && (method !== 'GET' ? data !== undefined : true) && headers?.authorization?.startsWith('Bearer ') ); when(fetchApi) .calledWith(isValidRequest) .mockResolvedValue({ status: 200, data: {} });组合2:表单验证链
const isEmail = when((email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)); const isStrongPassword = when((password: string) => password.length >= 8 && /[A-Z]/.test(password) && /[0-9]/.test(password) ); const isValidRegistration = when.allArgs(([email, password, confirm]: any[]) => isEmail(email) && isStrongPassword(password) && password === confirm ); when(authService.register) .calledWith(isValidRegistration) .mockResolvedValue({ userId: '123', token: 'abc' });📊 对比表:不同匹配方式的适用场景
| 匹配方式 | 适用场景 | 示例 | 性能 |
|---|---|---|---|
| 字面量匹配 | 精确参数值 | calledWith(42, 'text') | ⭐⭐⭐⭐⭐ |
| Jest不对称匹配器 | 类型/结构检查 | calledWith(expect.any(Number)) | ⭐⭐⭐⭐ |
| 函数匹配器 | 自定义条件逻辑 | calledWith(isEven) | ⭐⭐⭐ |
| allArgs匹配器 | 跨参数复杂逻辑 | calledWith(when.allArgs(...)) | ⭐⭐ |
🔍 调试技巧
1. 使用console.log调试匹配器
const debugMatcher = when((arg: any) => { console.log('Matching arg:', arg); return arg > 10; });2. 逐步构建复杂匹配器
// 先测试简单匹配器 const step1 = when((arg: any) => typeof arg === 'object'); // 添加更多条件 const step2 = when((arg: any) => step1(arg) && 'id' in arg);🎉 总结
jest-when的函数匹配器和allArgs功能为Jest测试带来了前所未有的灵活性。通过自定义匹配逻辑,你可以:
- 精确控制模拟函数的行为
- 简化复杂的参数校验逻辑
- 提高测试代码的可读性和可维护性
- 实现跨参数的复杂条件匹配
记住关键原则:从简单开始,逐步增加复杂度。对于大多数场景,Jest的内置匹配器已经足够;当遇到复杂逻辑时,再考虑使用函数匹配器或allArgs。
开始尝试这些高级功能,让你的单元测试更加精准和强大!💪
【免费下载链接】jest-whenJest support for mock argument-matched return values.项目地址: https://gitcode.com/gh_mirrors/je/jest-when
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考