纯函数天然适合单元测试,因其输入相同则输出必然相同,不依赖Date.now()、Math.random()等不可控值,不调用API、不操作DOM、不修改入参,无任何可观测副作用,测试只需验证“给什么→得什么”,无需mock、清理或等待。

纯函数天然适合单元测试,因为它的行为完全由输入决定,没有外部依赖、不修改状态、也不产生副作用。
纯函数的定义特征直接对应测试友好性
- 输入相同 → 输出必然相同(可重复验证)
- 不读取 Date.now()、Math.random()、localStorage 等不可控值
- 不调用 API、不操作 DOM、不修改入参对象
- 没有 console.log 或其他可观测副作用
这使得测试只需关注“给什么 → 得什么”,无需 mock、无需清理状态、无需等待异步。
怎么写纯函数让测试更简单
- 把外部依赖显式传入,而不是隐式使用 ```javascript // ✅ 好:时间可控 const logEvent = (message, now = Date.now()) => ({ message, timestamp: now });
// ❌ 难测:Date.now() 每次调用结果不同 const logEventBad = (message) => ({ message, timestamp: Date.now() });
- 避免修改原对象,返回新数据
```javascript
// ✅ 好:输入不变,输出确定
const updateName = (user, newName) => ({ ...user, name: newName });
// ❌ 难测:副作用不可控
const updateNameBad = (user, newName) => { user.name = newName; return user; };
-
用参数代替闭包或模块级变量
// ✅ 好:所有影响因素都在参数里 const calculateTax = (amount, rate, region) => amount * rate * regionMultiplier[region]; // ❌ 难测:regionMultiplier 是隐藏依赖 const calculateTaxBad = (amount, rate) => amount * rate * regionMultiplier[defaultRegion];
怎么测纯函数——三步到位
- 覆盖典型输入:正常值、边界值(空、零、极大极小)、非法类型(null、undefined) - 断言明确结果:用 `expect(result).toEqual(...)` 或 `toBe()` 直接比对返回值 - 不用 setup/teardown:无需 beforeEach、不需要 restore mocks
示例:
import { sum, filterEven, compose } from './utils';
describe('sum', () => {
test('returns 0 for empty array', () => {
expect(sum([])).toBe(0);
});
test('adds positive numbers correctly', () => {
expect(sum([1, 2, 3])).toBe(6);
});
test('handles negative numbers', () => {
expect(sum([-1, -2])).toBe(-3);
});
});
常见陷阱与绕过方式
- 意外的引用共享:即使函数“看起来纯”,若返回对象/数组并被外部修改,可能干扰后续测试 → 总是用 `toEqual` 而非 `toBe` 比较结构 - 浮点数精度问题:用 `toBeCloseTo` 替代 `toBe` - 深层嵌套对象比较慢:可加 `.toStrictEqual()` 显式要求严格相等(包括属性顺序、类型)
纯函数不是为了炫技,而是为了让每次 add(2, 3) 都稳稳等于 5——这种确定性,就是单元测试最想要的起点。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











