如何将测试与重构场景转化为?

摘要:让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自己迭代进化。这是一个很有意思的话题,值得展开说说。