资讯动态

test-driven-development - testing-anti-patterns

发布时间:2026/10/2 16:40:58 来源:尧图企业网站定制
测试反模式在以下情况加载此参考编写或更改测试、添加 mock或想在生产代码中添加仅测试使用的方法时。概述测试必须验证真实行为而不是 mock 行为。Mock 是隔离的手段不是被测试的对象。核心原则测试代码做了什么而不是 mock 做了什么。遵循严格的 TDD 可以防止这些反模式。铁律1. 绝不测试 mock 行为 2. 绝不向生产类添加仅测试使用的方法 3. 不理解依赖就绝不 mock反模式 1测试 Mock 行为违规示例// ❌ BAD: Testing that the mock existstest(renders sidebar,(){render(Page/);expect(screen.getByTestId(sidebar-mock)).toBeInTheDocument();});为什么这是错误的你在验证 mock 能工作而不是组件能工作测试在 mock 存在时通过不存在时失败关于真实行为什么也没告诉你你的合作伙伴的纠正“我们是在测试 mock 的行为吗”修复方法// ✅ GOOD: Test real component or dont mock ittest(renders sidebar,(){render(Page/);// Dont mock sidebarexpect(screen.getByRole(navigation)).toBeInTheDocument();});// OR if sidebar must be mocked for isolation:// Dont assert on the mock - test Pages behavior with sidebar present闸门函数在断言任何 mock 元素之前 问自己我是在测试真实组件行为还是仅仅测试 mock 是否存在 如果是在测试 mock 是否存在 停止 - 删除该断言或取消对组件的 mock 改为测试真实行为反模式 2生产代码中的仅测试方法违规示例// ❌ BAD: destroy() only used in testsclassSession{asyncdestroy(){// Looks like production API!awaitthis._workspaceManager?.destroyWorkspace(this.id);// ... cleanup}}// In testsafterEach(()session.destroy());为什么这是错误的生产类被仅测试代码污染如果在生产中意外调用会很危险违反 YAGNI 和关注点分离混淆对象生命周期与实体生命周期修复方法// ✅ GOOD: Test utilities handle test cleanup// Session has no destroy() - its stateless in production// In test-utils/exportasyncfunctioncleanupSession(session:Session){constworkspacesession.getWorkspaceInfo();if(workspace){awaitworkspaceManager.destroyWorkspace(workspace.id);}}// In testsafterEach(()cleanupSession(session));闸门函数在向生产类添加任何方法之前 问自己这个方法只被测试使用吗 如果是 停止 - 不要添加它 把它放在测试工具中 问自己这个类拥有该资源的生命周期吗 如果不是 停止 - 这个方法放在错误的类中反模式 3不理解就 mock违规示例// ❌ BAD: Mock breaks test logictest(detects duplicate server,(){// Mock prevents config write that test depends on!vi.mock(ToolCatalog,()({discoverAndCacheTools:vi.fn().mockResolvedValue(undefined)}));awaitaddServer(config);awaitaddServer(config);// Should throw - but wont!});为什么这是错误的被 mock 的方法有测试依赖的副作用写入配置过度 mock 以求安全破坏了实际行为测试因错误原因通过或神秘地失败修复方法// ✅ GOOD: Mock at correct leveltest(detects duplicate server,(){// Mock the slow part, preserve behavior test needsvi.mock(MCPServerManager);// Just mock slow server startupawaitaddServer(config);// Config writtenawaitaddServer(config);// Duplicate detected ✓});闸门函数在 mock 任何方法之前 停止 - 先别 mock 1. 问自己真实方法有什么副作用 2. 问自己这个测试依赖这些副作用中的任何一个吗 3. 问自己我完全理解这个测试需要什么吗 如果依赖副作用 在更低的层级 mock实际的慢速/外部操作 或者使用保留必要行为的测试替身 而不是测试所依赖的高级方法 如果不确定测试依赖什么 先用真实实现运行测试 观察实际需要发生什么 然后才在正确的层级添加最小 mock 红旗 - 我 mock 这个是为了安全 - 这个可能很慢最好 mock 掉 - 不理解依赖链就 mock反模式 4不完整的 Mock违规示例// ❌ BAD: Partial mock - only fields you think you needconstmockResponse{status:success,data:{userId:123,name:Alice}// Missing: metadata that downstream code uses};// Later: breaks when code accesses response.metadata.requestId为什么这是错误的部分 mock 隐藏结构假设- 你只 mock 了你了解的字段下游代码可能依赖你未包含的字段- 静默失败测试通过但集成失败- Mock 不完整真实 API 完整虚假信心- 测试没有证明任何关于真实行为的东西铁律Mock 现实中存在的完整数据结构而不仅仅是你当前测试使用的字段。修复方法// ✅ GOOD: Mirror real API completenessconstmockResponse{status:success,data:{userId:123,name:Alice},metadata:{requestId:req-789,timestamp:1234567890}// All fields real API returns};闸门函数在创建 mock 响应之前 检查真实 API 响应包含哪些字段 行动 1. 从文档/示例中检查实际的 API 响应 2. 包含系统下游可能消费的所有字段 3. 验证 mock 与真实响应模式完全匹配 关键 如果你在创建 mock你必须理解整个结构 当代码依赖被省略的字段时部分 mock 会静默失败 如果不确定包含所有已记录的字段反模式 5事后才想到集成测试违规示例✅ Implementation complete ❌ No tests written Ready for testing为什么这是错误的测试是实现的一部分不是可选的后续步骤TDD 本来会捕捉到这一点没有测试就不能声称完成修复方法TDD cycle: 1. Write failing test 2. Implement to pass 3. Refactor 4. THEN claim complete当 Mock 变得过于复杂警告信号Mock 设置比测试逻辑更长Mock 一切以让测试通过Mock 缺少真实组件拥有的方法Mock 改变时测试就破坏你的合作伙伴的问题“这里真的需要 mock 吗”考虑使用真实组件的集成测试通常比复杂的 mock 更简单TDD 防止这些反模式为什么 TDD 有帮助先写测试→ 迫使你思考你实际在测试什么看着它失败→ 确认测试测试真实行为而非 mock最小实现→ 不会有仅测试的方法混入真实依赖→ 在 mock 之前你看到测试实际需要什么如果你在测试 mock 行为你就违反了 TDD- 你在没有先看到测试对真实代码失败的情况下添加了 mock。快速参考反模式修复断言 mock 元素测试真实组件或取消 mock生产代码中的仅测试方法移到测试工具不理解就 mock先理解依赖最小化 mock不完整的 mock完全镜像真实 API事后才想到的测试TDD - 测试优先过度复杂的 mock考虑集成测试红旗断言检查*-mock测试 ID只在测试文件中调用的方法Mock 设置占测试的 50%移除 mock 时测试失败无法解释为什么需要 mock只是为了安全而 mock底线Mock 是隔离的工具不是要测试的对象。如果 TDD 揭示你在测试 mock 行为你就走错了。修复测试真实行为或者质疑你为什么一开始要 mock。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑