--- id: cacr92/WeReply/tdd-workflow version: "f2b1df9d" license: MIT install: manual updated: 2026-01-30 --- # Tdd Workflow — Tdd Workflow guides developers through test-first development using the red-green-refactor pattern: write failing tests, implement minimal code to pass, then refactor while maintaining test coverage. It covers unit and integration testing strategies for both Rust and TypeScript, with practical examples including validation logic, mocking external dependencies, and coverage verification tools. All new features and fixes must meet an 80% test coverage threshold. Publisher: cacr92 · Stars: 4 · Updated: 2026-01-30 Install (manual): `git clone https://github.com/cacr92/WeReply` ## SKILL.md # TDD 工作流 Skill ## 核心原则 **强制要求**:所有新功能、Bug 修复、重构必须达到 **80% 以上测试覆盖率**。 --- ## RED-GREEN-REFACTOR 循环 ### 1. RED(写测试,测试失败) 先写测试,验证测试会失败(因为功能尚未实现)。 ### 2. GREEN(实现代码,测试通过) 编写最小代码使测试通过。 ### 3. REFACTOR(重构代码,保持测试通过) 优化代码,保持所有测试通过。 --- ## 工作流程 ### 步骤 1:编写用户故事 描述期望的行为: ``` 作为用户,我希望能够创建新的饲料配方, 以便我可以保存和管理不同的配方方案。 验收标准: - 配方名称必填,长度 2-50 个字符 - 品种代码必填,必须是有效的品种 - 创建成功后返回配方 ID - 创建失败时显示错误消息 ``` ### 步骤 2:生成测试用例 覆盖正常路径和边界情况: **Rust 测试用例**: ```rust #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_create_formula_success() { // 正常创建配方 } #[tokio::test] async fn test_create_formula_empty_name() { // 配方名称为空 } #[tokio::test] async fn test_create_formula_name_too_long() { // 配方名称过长 } #[tokio::test] async fn test_create_formula_invalid_species() { // 品种代码无效 } #[tokio::test] async fn test_create_formula_database_error() { // 数据库连接失败 } } ``` **TypeScript 测试用例**: ```typescript describe('FormulaForm', () => { it('should create formula successfully', async () => { // 正常创建 }); it('should show error when name is empty', async () => { // 名称为空 }); it('should show error when name is too long', async () => { // 名称过长 }); it('should show error when species is invalid', async () => { // 品种无效 }); }); ``` ### 步骤 3:运行测试(RED) ```bash # Rust cargo test # TypeScript npm test ``` **预期结果**:测试失败(因为功能尚未实现)。 ### 步骤 4:实现代码(GREEN) 编写最小代码使测试通过: **Rust 实现**: ```rust pub async fn create_formula( &self, dto: CreateFormulaDto, ) -> Result { // 验证输入 if dto.name.is_empty() { return Err(anyhow!("配方名称不能为空")); } if dto.name.len() > 50 { return Err(anyhow!("配方名称过长")); } // 验证品种代码 self.validate_species_code(&dto.species_code).await?; // 插入数据库 let formula_id = sqlx::query!( "INSERT INTO formulas (name, species_code) VALUES (?, ?)", dto.name, dto.species_code ) .execute(&self.pool) .await? .last_insert_rowid(); Ok(formula_id) } ``` **TypeScript 实现**: ```typescript export const FormulaForm: React.FC = () => { const [form] = Form.useForm(); const handleSubmit = async (values: any) => { try { const result = await commands.createFormula(values); if (result.success) { message.success('创建成功'); } else { message.error(result.message); } } catch (error) { message.error(`创建失败: ${error}`); } }; return (
{/* 其他字段 */}
); }; ``` ### 步骤 5:再次运行测试 ```bash cargo test npm test ``` **预期结果**:所有测试通过。 ### 步骤 6:重构(REFACTOR) 优化代码,保持测试通过: ```rust // 提取验证逻辑 fn validate_formula_name(name: &str) -> Result<()> { if name.is_empty() { return Err(anyhow!("配方名称不能为空")); } if name.len() > 50 { return Err(anyhow!("配方名称过长")); } Ok(()) } pub async fn create_formula( &self, dto: CreateFormulaDto, ) -> Result { // 使用提取的验证函数 validate_formula_name(&dto.name)?; self.validate_species_code(&dto.species_code).await?; // 插入数据�� let formula_id = sqlx::query!( "INSERT INTO formulas (name, species_code) VALUES (?, ?)", dto.name, dto.species_code ) .execute(&self.pool) .await? .last_insert_rowid(); Ok(formula_id) } ``` ### 步骤 7:验证覆盖率 ```bash # Rust cargo tarpaulin --out Html # TypeScript npm run test:coverage ``` **要求**:覆盖率 >= 80%。 --- ## 测试类型 ### 1. 单元测试(Unit Tests) 测试独立函数和组件: **Rust**: ```rust #[test] fn test_validate_formula_name() { assert!(validate_formula_name("测试配方").is_ok()); assert!(validate_formula_name("").is_err()); assert!(validate_formula_name(&"a".repeat(100)).is_err()); } #[test] fn test_calculate_nutrition() { let material = Material { protein: 18.0, energy: 3200.0, ..Default::default() }; let result = calculate_nutrition(&material); assert_eq!(result.protein, 18.0); } ``` **TypeScript**: ```typescript describe('formatCurrency', () => { it('should format number as currency', () => { expect(formatCurrency(1234.56)).toBe('¥1,234.56'); }); }); ``` ### 2. 集成测试(Integration Tests) 测试 API 端点和数据库操作: **Rust**: ```rust #[tokio::test] async fn test_create_formula_integration() { let pool = setup_test_db().await; let repo = FormulaRepository::new(Arc::new(pool)); let dto = CreateFormulaDto { name: "测试配方".to_string(), species_code: "PIG".to_string(), }; let result = repo.create(dto).await; assert!(result.is_ok()); cleanup_test_db().await; } ``` ### 3. 手动测试 对于关键用户流程,进行手动测试: - 配方优化完整流程 - 预混料设计流程 - 报告生成流程 --- ## 最佳实践 ### 1. 测试用户行为而非实现 ✓ 正确:测试"点击提交按钮后显示成功消息" ✗ 错误:测试"调用 setState 设置 success 为 true" ### 2. 保持测试快速 单元测试应 < 100ms。 ### 3. Mock 外部依赖 Mock 数据库、API、文件系统等。 ### 4. 测试隔离 每个测试独立运行,互不影响。 ### 5. 测试错误路径 不仅测试正常情况,也要测试错误情况。 ### 6. 使用描述性的测试名称 ```rust #[test] fn test_validate_formula_name_rejects_empty_string() { } #[test] fn test_validate_formula_name_accepts_valid_chinese_name() { } ``` --- ## Mock 示例 ### Mock Tauri 命令(TypeScript) ```typescript import { vi } from 'vitest'; vi.mock('../bindings', () => ({ commands: { createFormula: vi.fn(), }, })); it('should create formula', async () => { vi.mocked(commands.createFormula).mockResolvedValue({ success: true, data: { id: 1 }, message: '创建成功', }); const result = await createFormula(dto); expect(result.success).toBe(true); }); ``` ### Mock 数据库(Rust) ```rust use mockall::mock; mock! { pub FormulaRepository { async fn create(&self, dto: CreateFormulaDto) -> Result; } } #[tokio::test] async fn test_with_mock() { let mut mock_repo = MockFormulaRepository::new(); mock_repo .expect_create() .returning(|_| Ok(1)); let service = FormulaService::new(Arc::new(mock_repo)); let result = service.create_formula(dto).await; assert!(result.is_ok()); } ``` --- ## 覆盖率验证 ### Rust ```bash # 安装 tarpaulin cargo install cargo-tarpaulin # 生成覆盖率报告 cargo tarpaulin --out Html --output-dir coverage # 查看报告 open coverage/index.html ``` ### TypeScript ```bash # 运行测试并生成覆盖率 npm run test:coverage # 查看报告 open coverage/index.html ``` --- ## 何时使用此 Skill - ✅ 添加新功能前 - ✅ 修复 Bug 前 - ✅ 重构代码前 - ✅ 用户明确要求测试时 - ✅ 代码审查时发现缺少测试 --- ## 与其他 Skill 的配合 - 与 `testing-strategy` skill 配合使用 - 与 `code-review` skill 配合进行测试审查 - 与 `refactoring` skill 配合确保重构安全 --- ## 提交前检查清单 - [ ] 所有测试通过 - [ ] 覆盖率 >= 80% - [ ] 无跳过的测试 - [ ] 测试命名清晰 - [ ] 边界情况已测试 - [ ] 错误路径已测试 - [ ] Mock 使用合理 - [ ] 测试独立运行 --- ## 常见陷阱 ### 1. 测试实现细节 ✗ 错误:测试组件的内部状态 ✓ 正确:测试用户可见的行为 ### 2. 测试之间有依赖 ✗ 错误:test2 依赖 test1 的数据 ✓ 正确:每个测试独立设置数据 ### 3. 过度 Mock ✗ 错误:Mock 所有依赖 ✓ 正确:只 Mock 外部依赖 ### 4. 忽略异步问题 ✗ 错误:忘记 `await` ✓ 正确:正确处理所有异步操作 --- ## 参考资源 - [Rust Testing Guide](https://doc.rust-lang.org/book/ch11-00-testing.html) - [Vitest Documentation](https://vitest.dev/) - [React Testing Library](https://testing-library.com/react) - 项目规范:`.claude/rules/07-testing-standards.md` [View on SkillFed](https://skillfed.io/cacr92/WeReply/tdd-workflow) · [View on GitHub](https://github.com/cacr92/WeReply)