yonatankarp/software-design-skills

tdd-anti-patterns

Use when auditing tests for TDD smells — phrases like "test smells", "flaky tests", "slow tests", "brittle tests", "test anti-patterns", "tests testing the framework", "over-mocking", "mystery guest", "fragile tests", "test breaks on every refactor".

查看源码

使用方法

  1. 复制安装命令,在自己的智能体环境安装前先检查源码包。
  2. 从原始文档核对所需工具、凭据和支持的宿主。
  3. 说明输入材料与期望交付物,并对照来源检查输出。
npx skills add https://github.com/yonatankarp/software-design-skills --skill tdd-anti-patterns

开始前需了解

  • 安装不包含模型订阅、API 额度或访问文件的权限。
  • 仓库星数和安装量反映活跃情况,不等于独立质量或安全测试。
仓库原始内容

按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。

One-line summary

A reference catalog of test-suite anti-patterns. Each smell has a symptom you can spot, an explanation of why it's wrong, and a pointer to the TDD technique that fixes it.

When to use this skill

  • Code review of test code.
  • A test suite has grown ugly and someone has to triage what to fix first.
  • Onboarding: explaining "we don't do X here, and here's why".
  • Diagnosing CI pain (slow, flaky, hard to debug).

When NOT to use this skill

  • You haven't written the tests yet — use the per-technique skills directly.
  • A specific test is failing for legitimate reasons — that's debugging, not auditing.

Core content

Test interdependence. Tests pass when run alone but fail when run together. Symptom: CI fails on the test that "always passes locally". Fix: tdd-test-isolation.

Slow suite. Single test takes longer than ~100 ms; whole suite takes hours. Symptom: developers stop running tests locally. Fix: push real I/O, real time, real network out behind doubles (tdd-mock-object); use in-memory fakes for unit tests.

Flaky tests. Same test fails sometimes, passes sometimes, with no code change. Symptom: "rerun the build" culture. Causes: race conditions, real clocks, non-deterministic order, network. Fix: inject the clock, inject the executor, seed randomness, isolate (tdd-test-isolation).

Mystery Guest. Test depends on data it didn't put there — a database row, a file on disk. Symptom: select * from users where id = 42 in the test assertion; no record of how 42 got there. Fix: the test sets up its own fixture.

Over-mocking. Tests verify which collaborators were called rather than what behavior was produced. Symptom: verify(mock, times(2)) everywhere; benign refactor breaks ten tests. Fix: mock only at the boundary (tdd-mock-object); use real objects inside your domain.

Testing the framework. A test that verifies, e.g., that @Transactional rolls back, or that Jackson serializes a LocalDate. Symptom: the test would pass even if the team's code didn't exist. Fix: delete it; the framework already tests itself.

Testing implementation, not behavior. A test asserts internal state (private field values, method call counts) rather than observable outcomes. Symptom: refactor that doesn't change behavior breaks dozens of tests. Fix: assert on outputs, return values, side effects to externals — not internals.

Mocking what you don't own. A test directly mocks a third-party SDK's class. Symptom: SDK upgrade silently breaks the mock's behavior; tests pass; production breaks. Fix: wrap the SDK behind a small interface you own; mock the interface; add a tdd-learning-test for the SDK.

No test on bug fix. A bug is fixed without a regression test. Symptom: the bug returns weeks later. Fix: tdd-regression-test — every bug fix gets a test.

Massive tests. A single test sets up 50 lines of fixture, exercises 10 behaviors, and asserts 20 outcomes. Symptom: when it fails, nobody knows why. Fix: split into small focused tests; one assertion per test as the default.

Test code without code review. Test code is treated as second-class — no review, no refactoring, no naming discipline. Symptom: test suite is messier than production code. Fix: treat test code like production code. The test suite is your design's executable specification.

Mocking your own data classes. mock<Order>() when you could Order(...). Symptom: mocks need to be told every property they have. Fix: construct real value objects.

Assertions hidden in helper methods. A test calls verifyEverything(...); the actual assertions live three layers deep. Symptom: failure message is "AssertionError" with no context. Fix: assertions belong in the test method; helpers should set up state, not assert it.

Random sleeps for synchronization. Thread.sleep(2000) to "wait for the async thing to complete". Symptom: slow tests + flaky tests + neither problem actually solved. Fix: use proper synchronization primitives, await futures, use kp-testing-coroutines virtual time for suspending code.

No assertion message. assertEquals(expected, actual) with no third-argument message. Symptom: CI failure shows two numbers, no clue what was being checked. Fix: assertion messages or per-line description.

Decision heuristics

  • Severity scales with reach: a slow test in CI affects every developer; a single brittle test in one file is a small fix.
  • Rerunning a flaky test until it passes is not a fix. Find the source of nondeterminism.
  • Mocks added "to make the test fast" are the most common over-mocking on-ramp. Question every new mock.
  • Tests as documentation: the test's name and shape should tell you what the code does. If they don't, the test is broken regardless of pass/fail.

Anti-patterns about catching anti-patterns

  • Treating every long test as bad — sometimes integration tests are legitimately longer.
  • Demanding zero mocks — boundary mocks are correct and necessary.
  • Refactoring tests "for cleanliness" without re-running them. Test code is code; the same discipline applies.

See also

  • All eleven other tdd-* skills — each fix references the relevant technique.
  • kp-testing-coroutines — Coroutines-specific test smells.
  • kp-anti-patterns — Kotlin language smells (often appear in test code too).

References

  • Kent Beck, Test-Driven Development By Example, Ch. 27 — many of the test anti-patterns are mentioned alongside the patterns.
  • Gerard Meszaros, xUnit Test Patterns — exhaustive smell catalog (the canonical reference).
  • Robert C. Martin, Clean Code, Ch. 9 — clean tests; FIRST principles (Fast, Independent, Repeatable, Self-validating, Timely).

按任务相关推荐

来自同一仓库

更多 Skills

全部 Skills
yonatankarp
社区

arch-adr

Use when recording an architectural decision so future maintainers know why — phrases like "ADR", "architecture decision record", "why did we pick X", "lightweight architecture documentation", "decision log", "should this be an ADR", "tribal knowledge", "we chose this two years ago and now nobody remembers why". Defines the ADR as a short, structured document capturing what was decided, why, alternatives considered, and consequences — written at the time of the decision and kept in the repo.

安装量
2
GitHub Stars
1
最近更新
5月19日
yonatankarp
社区

arch-fitness-functions

Use when enforcing architectural characteristics through automation — phrases like "architecture fitness function", "evolutionary architecture", "architecture tests", "ArchUnit", "dependency rules in CI", "automated architecture checks", "guardrails", "drift detection". Defines fitness functions as automated checks that verify architectural characteristics on every change, catching drift before it accumulates.

安装量
2
GitHub Stars
1
最近更新
5月19日
yonatankarp
社区

arch-microservices

Use when designing or reviewing a microservices architecture — phrases like "microservices", "should we split this service", "service boundaries", "bounded context per service", "API gateway", "backend for frontend", "BFF", "service mesh", "microservices vs monolith", "database per service", "data ownership". Defines microservices as independently deployable services aligned with business capabilities — and the discipline of resisting the style until the monolith proves inadequate.

安装量
2
GitHub Stars
1
最近更新
5月19日
yonatankarp
社区

arch-strangler-fig

Use when migrating a legacy system incrementally — phrases like "strangler fig", "strangler pattern", "incremental migration", "monolith to microservices", "rewrite without big bang", "replace legacy in place", "facade in front of the legacy system", "anticorruption layer". Defines Fowler's Strangler Fig as the migration discipline of gradually replacing legacy functionality behind a stable facade, never doing a big-bang rewrite.

安装量
2
GitHub Stars
1
最近更新
5月19日