单元测试中必须用深拷贝隔离测试数据,以避免共享引用导致的状态污染;推荐用lodash的clonedeep预处理fixture,复杂类型可配合clonedeepwith定制逻辑,或依环境选用structuredclone等轻量方案。

单元测试中隔离测试数据,核心是确保每次测试用的都是全新、互不干扰的对象实例。深拷贝正是实现这一点的关键手段——它切断原始数据与测试用例之间的引用链,避免状态污染。
为什么测试必须用深拷贝
测试数据常来自共享的 fixture 或 mock 数据源。如果只做浅拷贝或直接赋值,多个测试用例会操作同一堆内存中的对象。比如修改某个测试里的 user.profile.address.city,可能意外影响后续测试的断言结果。
常见踩坑场景:
- 多个
it块复用同一个对象字面量,未重置嵌套属性 - 使用
Object.assign({}, data)或展开运算符,但 data 含有嵌套对象或数组 - 依赖
JSON.parse(JSON.stringify()),却忽略了日期、正则、undefined或循环引用导致的数据失真
推荐做法:用 cloneDeep 预处理 fixture
在测试文件顶部统一定义原始 fixture,每次测试前深拷贝一份:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
import { cloneDeep } from 'lodash';
const baseUser = {
id: 1,
name: 'Alice',
profile: {
birthDate: new Date('1990-05-12'),
tags: ['admin', 'active']
}
};
describe('User service', () => {
it('should update user name', () => {
const user = cloneDeep(baseUser); // 每次都拿到干净副本
user.name = 'Bob';
expect(user.name).toBe('Bob');
expect(baseUser.name).toBe('Alice'); // 原始 fixture 不变
});
});
进阶技巧:配合自定义克隆逻辑
当 fixture 含特殊类型(如 Date、Map、类实例),原生 cloneDeep 可能不够。这时可用 cloneDeepWith 注入定制规则:
- 把
Date实例转为 ISO 字符串再重建,保证可序列化 - 跳过某些只读字段(如
createdAt),避免测试时被意外覆盖 - 对函数字段返回空桩(
jest.fn()),便于后续 mock
示例:
import { cloneDeepWith } from 'lodash';
const safeClone = (value) => {
if (value instanceof Date) return new Date(value.getTime());
if (typeof value === 'function') return jest.fn();
return undefined; // 让 lodash 继续默认处理其他类型
};
it('handles date and function fields correctly', () => {
const user = cloneDeepWith(baseUser, safeClone);
expect(user.profile.birthDate).toBeInstanceOf(Date);
});
替代方案对比与取舍
不总要引入 lodash。轻量级场景可考虑:
-
结构简单且无特殊类型:用
structuredClone(现代浏览器和 Node.js 17+ 支持),原生、安全、支持Date、RegExp、Map、Set等 - 需兼容老环境:封装一个最小化深拷贝工具,只递归处理 plain object 和 array,避开函数、Symbol、循环引用等边界情况
-
fixture 极度固定:直接在每个
it中重新构造对象字面量,语义清晰,无拷贝开销
关键是根据项目实际约束选最稳的那一种,而不是追求“最通用”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










