柯里化是将接收多个参数的函数转换为一系列每次只接收一个参数的函数的过程,例如 add(a, b, c) 可变为 a => b => c => a + b + c,支持参数预置、提升复用性与测试隔离性。

JavaScript 中柯里化本身不直接“实现单元测试参数注入”,但它是一种非常实用的函数式编程技巧,能自然地支持测试时的参数预置与隔离,从而让测试更简洁、可复用、易维护。
什么是柯里化(Currying)?
柯里化是将一个接收多个参数的函数,转换为一系列每次只接收一个参数的函数的过程。例如:
const add = (a, b, c) => a + b + c; const curriedAdd = a => b => c => a + b + c; // 使用 add(1, 2, 3); // 6 curriedAdd(1)(2)(3); // 6
为什么柯里化适合单元测试中的参数注入?
在测试中,我们常需固定某些依赖(如 API 地址、超时时间、mock 的工具函数),而只变动待测逻辑的输入。柯里化天然支持「提前绑定部分参数」,把环境/配置类参数固化,留下纯业务输入供测试用例灵活传入。
- 避免每个测试用例重复传入相同配置
- 提升测试函数的专注度:只关注输入 → 输出
- 便于组合不同测试场景(如 success / error / timeout)
- 和 Jest/Mocha 等框架无缝配合,无需额外包装
实际例子:测试一个带配置的 HTTP 请求函数
假设你有一个通用请求函数,需要 base URL 和超时时间:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
// 源函数(非柯里化)
const fetchUser = (baseUrl, timeout, userId) =>
fetch(`${baseUrl}/users/${userId}`, { signal: AbortSignal.timeout(timeout) });
// 柯里化版本(推荐用于测试)
const createFetcher = (baseUrl) => (timeout) => (userId) =>
fetch(`${baseUrl}/users/${userId}`, { signal: AbortSignal.timeout(timeout) });
// 测试时轻松注入固定配置
const testFetcher = createFetcher('https://api.test')(5000);
// 各个测试用例只需关心 userId 和 mock 行为
test('fetches user 123', async () => {
jest.mock('node-fetch');
global.fetch.mockResolvedValueOnce({ json: () => ({ id: 123 }) });
const res = await testFetcher(123);
expect(res.id).toBe(123);
});
test('throws on user 999', async () => {
global.fetch.mockRejectedValueOnce(new Error('Not found'));
await expect(testFetcher(999)).rejects.toThrow();
});
进阶技巧:结合 Jest 的 mockImplementation 替换依赖
柯里化后,你可以把外部依赖(如 fetch、localStorage)也作为最外层参数传入,实现彻底的纯函数测试:
// 完全可测试的柯里化函数
const fetchUserPure = (fetchImpl) => (baseUrl) => (timeout) => (userId) =>
fetchImpl(`${baseUrl}/users/${userId}`, { signal: AbortSignal.timeout(timeout) });
// 测试时注入 mock fetch
const mockFetch = jest.fn().mockResolvedValue({ json: () => ({ id: 456 }) });
const testFetch = fetchUserPure(mockFetch)('https://api.dev')(3000);
test('calls fetch with correct URL and options', async () => {
await testFetch(456);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.dev/users/456',
{ signal: expect.any(AbortSignal) }
);
});
不复杂但容易忽略:柯里化不是为了炫技,而是为了让「变化的」和「不变的」参数在调用链上清晰分离——这正是高质量单元测试的核心诉求之一。

















