公司要求单元测试覆盖率 80%,我看着那几千行的屎山组件,眼泪掉下来。手写?写到下个月。测试妹子天天在群里 @ 我:“帅哥,你的单测呢?” 我灵机一动,让 AI 帮我写。一天后,测试覆盖率 91%,0 bug,测试妹子发了条朋友圈:“某前端切图仔突然开窍了。” 我没敢告诉她,那是 GPT-5.5 的手笔。
写单测这事,比写业务代码还痛苦。业务代码至少能跑,能看到按钮,能点。单测?Mock 一堆,断言一堆,跑起来全红,改到怀疑人生。尤其接手老项目,一个组件几百行,手动补测试?告辞。
但我发现,AI 写单测的水平已经超过大部分初级工程师。你只要给它组件代码,它能给你生成结构清晰的测试用例:渲染测试、交互测试、异步测试、边界测试,甚至能帮你 mock 掉依赖的 API。
今天我就教你用 AI(Copilot / ChatGPT / Cursor)自动生成高质量 Jest + Testing Library 测试,把一周的活压缩到一天。测试妹子开心,我也能准时下班。
缺点:生成的测试可能不够精准(比如断言了不重要的属性),需要你微调。但总比从零开始强。
我们用一个实际的组件:UserProfile,它接收 userId,内部 fetch 用户数据,显示 loading、error、和用户信息。
// UserProfile.jsx
import { useState, useEffect } from 'react';
import { fetchUser } from './api';
export function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetchUser(userId)
.then(setUser)
.catch(setError)
.finally(() => setLoading(false));
}, [userId]);
if (loading) return <div>加载中...</div>;
if (error) return <div>出错了:{error.message}</div>;
if (!user) return <div>无数据</div>;
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
或者在 ChatGPT 里粘贴代码,加上提示词:
请为以下 React 组件生成 Jest + React Testing Library 的单元测试。需要覆盖:初始加载状态、成功获取数据后显示用户信息、请求失败时显示错误信息、loading 结束后的渲染。请 mock 掉
fetchUser这个 API。
import { render, screen, waitFor } from '@testing-library/react';
import { UserProfile } from './UserProfile';
import { fetchUser } from './api';
// Mock API 模块
jest.mock('./api', () => ({
fetchUser: jest.fn(),
}));
describe('UserProfile', () => {
beforeEach(() => {
jest.clearAllMocks();
});
test('显示 loading 状态', () => {
fetchUser.mockImplementation(() => new Promise(() => {})); // 永不返回
render(<UserProfile userId={1} />);
expect(screen.getByText('加载中...')).toBeInTheDocument();
});
test('成功加载后显示用户信息', async () => {
const mockUser = { name: '张三', email: 'zhangsan@example.com' };
fetchUser.mockResolvedValue(mockUser);
render(<UserProfile userId={1} />);
await waitFor(() => {
expect(screen.getByText('张三')).toBeInTheDocument();
expect(screen.getByText('zhangsan@example.com')).toBeInTheDocument();
});
});
test('请求失败时显示错误信息', async () => {
const error = { message: '网络错误' };
fetchUser.mockRejectedValue(error);
render(<UserProfile userId={1} />);
await waitFor(() => {
expect(screen.getByText(/出错了:网络错误/)).toBeInTheDocument();
});
});
});
看,连等待异步的 waitFor 都帮你写好了。 你只需要改一下错误信息的正则,或者加几个边界测试(比如 userId 为空)。
你可以在提示词里详细要求:
除了基本测试,还需要测试:
- 当 userId 变化时,会重新请求数据(之前的请求应被忽略或取消)
- 组件卸载时,不应再调用 setState
- 快照测试(可选)
AI 会给你生成类似:
test('userId 变化时重新获取数据', async () => {
const mockUser1 = { name: 'User1' };
const mockUser2 = { name: 'User2' };
fetchUser.mockResolvedValueOnce(mockUser1);
const { rerender } = render(<UserProfile userId={1} />);
await waitFor(() => expect(screen.getByText('User1')).toBeInTheDocument());
fetchUser.mockResolvedValueOnce(mockUser2);
rerender(<UserProfile userId={2} />);
await waitFor(() => expect(screen.getByText('User2')).toBeInTheDocument());
expect(fetchUser).toHaveBeenCalledTimes(2);
});
连 rerender 都懂,你还想怎样?
| 步骤 | 手写时间 | AI 时间 |
|---|---|---|
| 写第一个测试(渲染 + loading) | 10min | 5s |
| 写成功场景 + mock | 15min | 5s |
| 写错误场景 | 10min | 5s |
| 写 userId 变化场景 | 15min | 5s |
| 调试断言 | 20min | 5min |
| 总计 | 70min | ~6min |
速度提升 10 倍以上。而且 AI 不会忘记清理 mock、不会漏掉 waitFor。
接手一个电商项目的购物车模块,几乎没测试。我写了脚本,把每个组件文件内容喂给 GPT-4(分批,用 API),让它输出测试代码。然后手动改几个断言,跑一遍。两个晚上,覆盖率从 12% 升到 86%。测试妹子在群里发了个烟花表情,我回了个狗头。
jest.mock('./api') 如果你的 api 文件是 services/api.js,需要手动改。waitFor 的超时,有些场景需要增加 timeout 配置。div 的 className),需要你删除。toMatchSnapshot(),但快照容易 “假绿”,建议换成具体的断言。最后送大家一句话:AI 写单测,你喝咖啡。测试全绿,准点下班。
评论区聊聊:你试过用 AI 写单测吗?有没有翻车?