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 한도, 파일 접근 권한이 포함되지 않습니다.
  • 별점 수와 설치 수는 활동 지표이며 독립적인 품질·보안 검증이 아닙니다.
원본 Skill 문서

원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.

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

모든 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일