如何将测试与重构场景转化为?
摘要:让AI正确帮你写测试和重构代码 测试这个事 说句大实话,写测试这件事,大部分程序员都是拒绝的。不是不知道测试的重要性,实在是业务代码都写不过来,哪有那么多时间写测试。 但自从我用上AI编程工具之后,测试写得比以前勤多了。原因很简单:让AI帮
让AI正确帮你写测试和重构代码
测试这个事
说句大实话,写测试这件事,大部分程序员都是拒绝的。不是不知道测试的重要性,实在是业务代码都写不过来,哪有那么多时间写测试。
但自从我用上AI编程工具之后,测试写得比以前勤多了。原因很简单:让AI帮我写测试,比我自己写快多了。当然前提是,你得告诉AI测试应该怎么写。
测试场景的AGENTS.md怎么写
# AGENTS.md - 测试场景配置
## 测试类型定义
### 单元测试
- 位置:`src/**/*.test.ts` 或 `tests/unit/`
- 框架:Vitest / Jest
- 要求:每个导出函数/类必须有对应测试
- 覆盖率目标:70%以上
### 集成测试
- 位置:`tests/integration/`
- 使用测试数据库/环境
- 测试前后自动清理测试数据
- 覆盖核心业务流程
### E2E 测试
- 位置:`tests/e2e/`
- 使用 Playwright / Cypress
- 覆盖关键用户路径
- 在 CI 中运行
## 测试命令
```bash
# 运行所有测试
npm test
# 监听模式(开发时使用)
npm test -- --watch
# 运行特定文件
npm test -- src/utils/helper.test.ts
# 运行特定标签的测试
npm test -- --grep "unit"
# 生成覆盖率报告
npm test -- --coverage
# E2E 测试
npm run test:e2e
测试数据管理
测试夹具(Fixtures)
公共测试数据放在 tests/fixtures/ 目录
使用工厂函数生成测试数据
避免硬编码测试数据
Mock 策略
外部 API 使用 MSW (Mock Service Worker)
数据库使用测试数据库
第三方服务使用 jest.mock()
示例:用户创建测试
// tests/unit/user.service.test.ts
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { UserService } from '../../src/services/user.service';
import { UserRepository } from '../../src/repositories/user.repository';
describe('UserService', () => {
let userService: UserService;
let mockRepository: UserRepository;
beforeEach(() => {
// 创建 mock
mockRepository = {
create: vi.fn(),
findById: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
} as any;
// 注入 mock
userService = new UserService(mockRepository);
});
it('should create a user', async () => {
const userData = { name: 'John', email: 'john@example.com' };
const createdUser = { id: '1', ...userData };
mockRepository.create.mockResolvedValue(createdUser);
const result = await userService.createUser(userData);
expect(result).toEqual(createdUser);
expect(mockRepository.create).toHaveBeenCalledWith(userData);
});
});
AI 辅助测试指南
生成测试时
先了解现有测试风格,模仿着写
使用真实数据模拟而非硬编码值
测试边界条件和异常情况
为异步代码编写正确的异步测试
测试命名规范
// 好的命名
describe('UserService', () => {
it('should create user with valid data', () => {});
it('should throw error with invalid email', () => {});
it('should return user by id', () => {});
});
// 不好的命名
describe('UserService', () => {
it('test1', () => {});
it('create', () => {});
});
必须覆盖的场景
正常业务流程
边界条件(空值、零值、极大值)
异常情况(网络错误、超时、权限不足)
并发场景(如果适用)
注意事项
测试文件与源文件放在相同目录或 __tests__ 目录
使用 describe/test 结构组织测试
每个测试只测试一件事
测试名称使用描述性名称
避免测试之间的依赖
失败的测试必须立即修复
## 代码重构场景
重构这个事,风险很高。改好了皆大欢喜,改差了就是事故现场。让AI帮忙重构,必须得把规矩定清楚。
```markdown
# AGENTS.md - 代码重构场景配置
## 重构原则
1. **小步前进**:每次只重构一小部分,改完立即测试
2. **保持功能**:重构后必须通过所有测试,功能行为不变
3. **频繁提交**:每个重构步骤单独提交,便于回滚
4. **红绿重构**:先写失败的测试,再改代码让测试通过
## 重构前检查清单
```bash
# 确保测试全部通过
npm test
# 检查类型错误
npm run typecheck
# 运行 lint
npm run lint
# 确保没有未提交的更改
git status
常见重构类型指南
1. 命名重构
使用有意义的变量/函数名
遵循语言命名规范(PascalCase, camelCase)
避免缩写(除非是公认的,如 id, url, api)
函数名应该表达意图
2. 函数重构
单一职责:每个函数只做一件事
参数控制:不超过 3 个参数
行数控制:单个函数不超过 50 行
提取重复代码为独立函数
3. 类重构
遵循 SOLID 原则
依赖注入代替直接依赖
使用接口抽象具体实现
避免上帝类(God Class)
4. 模块重构
高内聚、低耦合
循环依赖必须消除
使用 barrel 文件导出公共 API
明确模块的公开接口
5. 条件语句重构
优先使用多态而非 switch/if
使用策略模式替代复杂条件
提早返回减少嵌套
重构安全规则
禁止事项
禁止在重构时添加新功能
禁止改变现有 API 行为
禁止跳过测试直接提交
禁止大规模重写
禁止删除测试
必须事项
重构前必须创建备份分支
重构前必须确认所有测试通过
重构后必须重新运行所有测试
必须使用 refactor 类型的 commit message
回滚策略
# 如果重构出问题,快速回滚
git stash
git checkout backup-branch
# 或者使用 git reflog 找回重构前的状态
git reflog
git reset --hard HEAD@{n}
注意事项
重构前确保有足够的测试覆盖
大型重构拆分成多个小步骤
重构完成后更新相关文档
复杂重构先在本地验证
重要重构需要代码审查
## 实际案例:让AI帮忙写测试
我用一个真实例子来说明测试场景下AGENTS.md的作用。
假设你有一个用户服务,里面有个注册功能。你让AI帮你写测试,如果你没有AGENTS.md,AI可能给你写出这样的测试:
```typescript
it('test register', () => {
const result = register({ name: 'John', email: 'john@test.com' });
expect(result).toBeDefined();
});
这个测试有啥用?基本上啥也验证不了。
但如果你在AGENTS.md里写清楚了测试规范,AI就能写出这样的测试:
describe('UserService.register', () => {
it('should create user with valid data', async () => {
const input = { name: 'John', email: 'john@test.com', password: 'Password123!' };
const result = await userService.register(input);
expect(result.id).toBeDefined();
expect(result.email).toBe(input.email);
expect(result.password).not.toBe(input.password); // 密码应该被哈希
});
it('should throw error with duplicate email', async () => {
const input = { name: 'John', email: 'exists@test.com', password: 'Password123!' };
await userService.register(input);
await expect(userService.register(input)).rejects.toThrow('Email already exists');
});
it('should throw error with invalid email', async () => {
const input = { name: 'John', email: 'invalid', password: 'Password123!' };
await expect(userService.register(input)).rejects.toThrow('Invalid email format');
});
it('should throw error with weak password', async () => {
const input = { name: 'John', email: 'john@test.com', password: '123' };
await expect(userService.register(input)).rejects.toThrow('Password too weak');
});
});
看到了吗?有AGENTS.md和没有AGENTS.md,AI写出来的测试质量完全不在一个水平上。
小结
测试和重构场景下的AGENTS.md,关键在于把规范写清楚。测试要覆盖哪些场景、重构要遵守什么规则,这些都说清楚了,AI才能帮你写出高质量的测试代码和安全可靠的重构。
下期聊聊高级技巧:怎么让AGENTS.md自己迭代进化。这是一个很有意思的话题,值得展开说说。
