Skill test-driven-development
Terapkan prinsip test-driven development untuk merancang software melalui contract-based testing dan manajemen architectural boundary.
- Bintang GitHub
- 9
- File
- 1
- Commit terakhir
- 13 Okt 2026
- Lisensi
- EPL-2.0
- Tool yang diizinkan
- —
Apa fungsi skill test-driven-development?
test-driven-development adalah agent skill. Terapkan prinsip test-driven development untuk merancang software melalui contract-based testing dan manajemen architectural boundary.
Ringkasan dibuat dari SKILL.md skill ini.
Tentang skill test-driven-development
Menerapkan prinsip test-driven development ke dalam desain software adalah hal yang dimungkinkan oleh skill ini bagi developer maupun AI agent. Skill ini memperlakukan test sebagai kontrak formal yang mendefinisikan business requirements dan expected behavior, bukan sekadar tambahan di akhir proses. Saat sebuah agent memuat skill ini, ia akan memberikan panduan tentang cara menulis failing tests terlebih dahulu, mengelola architectural boundaries, dan menggunakan test doubles yang tepat seperti stubs, mocks, fakes, atau spies.
User dapat mengandalkan skill ini untuk menjaga kualitas kode yang tinggi dengan berfokus pada perbaikan implementasi daripada mengubah test agar sesuai dengan kode yang rusak. Skill ini membantu mengidentifikasi design smells, seperti keinginan untuk melakukan mock pada internal private methods, dan menyarankan refactoring sebagai solusinya. Skill ini mencakup berbagai jenis architectural boundaries termasuk layer, module, process, dan external system boundaries. Ini sangat berguna saat bekerja dengan framework spesifik seperti JUnit, pytest, atau clojure.test untuk memastikan bahwa unit tests memverifikasi logika komponen sementara integration tests memverifikasi perilaku sistem yang sebenarnya di berbagai boundaries.
Apa yang bisa dilakukan agent dengannya
- 1Debug failing testsGunakan kegagalan test sebagai sinyal untuk melakukan debug pada implementasi daripada memodifikasi test assertions.
Ditulis dari SKILL.md milik skill tersebut. Setiap use case diverifikasi dengan kutipan darinya.
Cara install skill test-driven-development
Setiap perintah menyalin folder skill ke direktori skills client. Restart client atau mulai sesi baru untuk memuatnya.Install skill test-driven-development di Plugin
/plugin marketplace add Pyroxin/opinionated-claude-skills /plugin install opinionated-software-engineering@opinionated-claude-skills
Install skill test-driven-development di Claude Code
~/.claude/skills/test-driven-development/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/test-driven-development ~/.claude/skills/test-driven-developmentInstall skill test-driven-development di Claude Code (project)
.claude/skills/test-driven-development/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/test-driven-development .claude/skills/test-driven-developmentInstall skill test-driven-development di Codex
~/.agents/skills/test-driven-development/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/test-driven-development ~/.agents/skills/test-driven-developmentInstall skill test-driven-development di Gemini CLI
~/.gemini/skills/test-driven-development/SKILL.md
$ npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/test-driven-development ~/.gemini/skills/test-driven-developmentInstall skill test-driven-development di Claude.ai
npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/test-driven-development test-driven-development && zip -r test-driven-development.zip test-driven-development # claude.ai → Customize → Skills → + → Create skill → Upload a skill → test-driven-development.zip
SKILL.md
Lihat di GitHubTest-Driven Development
For system-level design principles and architectural boundaries, see the software-engineer skill.
Core Philosophy
<core_philosophy>
Foundation: TDD is an application of contract-based design. Tests define the contract an implementation must fulfill—they are specifications, not afterthoughts. This connects directly to the abstraction principles in the software-engineer skill.
Tests = Contracts: Tests represent business requirements and expected behavior. They are contracts the implementation must fulfill. Never compromise test integrity to achieve green tests.
Tests are source of truth: When tests fail, fix the implementation. Only change tests when requirements change or test has a verified bug. </core_philosophy>
Testing Approach
Architectural Boundaries and Mocking Strategy
<architectural_boundaries> Identify architectural boundaries in your system:
- Layer boundaries (e.g., data layer, service layer, presentation layer)
- Module boundaries (e.g., namespaces, packages, crates)
- Process boundaries (e.g., Erlang processes, Elixir GenServers)
- External system boundaries (e.g., APIs, databases, third-party services) </architectural_boundaries>
<unit_definition> What constitutes a "unit":
A "unit" is a cohesive component at an architectural boundary:
- Classes (e.g., Java, Python, Swift)
- Namespaces with related functions (e.g., Clojure, Haskell)
- Modules with related predicates (e.g., Prolog)
- Processes/GenServers (e.g., Erlang, Elixir)
- Modules with traits/protocols (e.g., Rust, Swift) </unit_definition>
<mocking_rules> When to mock:
- Mock dependencies across architectural boundaries
- External dependencies injected/passed into system under test get mocked
When NOT to mock:
- Don't mock the system under test itself
- Don't mock within a cohesive unit (e.g., private methods, internal helpers)
- If tempted to mock internals, that's a design smell — refactor instead </mocking_rules>
<test_balance> Balance unit and integration tests:
- Unit tests: Test each component in isolation with mocked dependencies across boundaries
- Integration tests: Test behavior across boundaries with real implementations
- Both are necessary — unit tests verify component logic, integration tests verify system behavior
- Over-mocked tests validate mocks, not real behavior </test_balance>
┌───────────────────────────────────────────────────────┐
│ Application Core │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Domain │ │ Service │ │ Use Case │ │
│ │ Models │ │ Layer │ │ Layer │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
├───────────────────────BOUNDARY────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Database │ │ External │ │ HTTP │ │
│ │ Adapter │ │ API │ │ Client │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└───────────────────────────────────────────────────────┘
- Unit tests: Test domain models and service layer with mocked adapters
- Integration tests: Test adapters with real external systems (DB, APIs)
- Contract tests: Verify adapters fulfill the port interface
- External database gets mocked at adapter boundary, not inside services
Test Doubles Taxonomy
<test_doubles> Understanding test double types aids in communicating test intent. This taxonomy was formalized by Gerard Meszaros in xUnit Test Patterns (2007):1
| Type | Purpose | When to Use |
|---|---|---|
| Stub | Returns predetermined values | When behavior doesn't affect test outcome |
| Mock | Verifies interactions occurred | When the interaction IS the behavior being tested |
| Fake | Working implementation (simplified) | When real implementation too slow/complex |
| Spy | Records calls for later verification | When need to verify after execution |
Key insight: Overuse of mocks often indicates testing implementation rather than behavior. Prefer stubs when possible; use mocks when interactions are the contract.
Test Design Process
<design_steps> When designing tests:
- Identify architectural boundaries in the system
- Determine what constitutes a "unit" (component, layer, module)
- Plan unit tests for each unit with dependencies mocked at boundaries
- Plan integration tests crossing boundaries with real implementations
- Ensure tests verify usage/requirements, not implementation details </design_steps>
<failure_response> When test fails:
- Read test to understand expected behavior
- Debug implementation to find why it doesn't meet expectations
- Fix implementation, never "fix" test to pass </failure_response>
<quality_criteria> Test quality requirements:
- Descriptive names explaining the requirement being checked
- Readable as documentation of system behavior
- Plan test cases based on usage, not implementation details
- Include comprehensive documentation (e.g., Javadoc for JUnit, Sphinx for Python) explaining what test ensures and special considerations
- Tests can have bugs or enforce wrong behaviors—always assess correctness </quality_criteria>
Test-Driven Development Workflow
<tdd_cycle>
- Write failing tests FIRST based on requirements (including desired usage patterns)
- Thoroughly document planned tests to guide implementation
- Stub planned tests with exceptions:
throw new IllegalStateException("Not implemented yet!")
- Implementation makes existing tests pass without test modification
- Tests are source of truth — never change tests to match broken code
- When debugging:
- Understand why test is failing
- Fix implementation to match test expectations
- Only modify tests if requirements changed or test has VERIFIED bug
- Treat test failures as implementation bugs, not test bugs
- Test bugs exist, but assume only after verifying implementation is correct </tdd_cycle>
<debugging_tip> Debug and trace logging: Effective way to probe program function during test execution. </debugging_tip>
Critical Anti-Patterns
<anti_patterns> Never:
- Mock the system under test itself (the component being tested)
- Mock within a cohesive unit (e.g., private methods, internal helpers of the unit under test)
- Change test assertions to match current behavior
- Write tests that mirror implementation rather than requirements
- Over-mock to the point tests don't validate real behavior
- Write tests that don't exercise code in module being tested (e.g., calculating result in test instead of using module-under-test)
- Mock to make tests pass — fix implementation instead </anti_patterns>
<design_smell> If tempted to mock internals of the unit under test: That's a design smell — refactor the code instead. </design_smell>
Framework-Specific Guidance
See language-specific skills for testing framework details:
- java-programmer: JUnit, Mockito, assertion patterns
- python-programmer: pytest, unittest, mocking strategies
- clojure-programmer: clojure.test, test.check
- racket-programmer: RackUnit, testing patterns
General principles across frameworks:
- Use framework's assertion methods instead of custom comparison logic
- Include descriptive assertion messages explaining business/technical reasons
- Organize tests into logical categories/suites
- Maintain consistent assertion styles within codebase
Planning and Scaffolding
<scaffolding_steps> Before implementing:
- Create interfaces, type signatures, abstract classes, and other definitional structures
- Create empty functions with step-by-step comments: "1. Setup data.", "2. Process data.", etc.
- Write tests after defining but NOT implementing code structures to create example API uses
- Examine example code for potential usage problems requiring scaffold refactoring </scaffolding_steps>
<maintainability_principles> Maintainability:
- Assume code maintained by junior engineers and other LLMs
- Prefer strongly defined structures based on idiomatic patterns for the language
- If uncertain, ask — user is senior engineer with answers or resources </maintainability_principles>
Common Mistakes
<common_mistakes>
Mechanical Mistakes
- Planning tests based on implementation instead of usage/requirements
- Creating tests that over-fit to specific implementation
- Removing existing documentation or comments (rephrase for consistency instead)
- Not documenting tests (every test needs comprehensive documentation)
- Using inconsistent assertion/verification styles across tests
- Not explaining business/technical reasons in assertion messages
Judgment Mistakes (Staff-Level Insights)
- Confusing "tests first" with "tests drive design": TDD is about using tests to discover design, not just writing tests before code
- Treating coverage metrics as quality proxies: 100% coverage with bad tests is worse than 70% coverage with good tests
- Not recognizing test difficulty as design feedback: If a unit is hard to test, the design is likely wrong—don't fight to mock, refactor instead
- Writing tests that specify implementation sequence: Tests should specify WHAT behavior occurs, not HOW or in what order
- Testing internal state rather than observable behavior: Tests should verify outputs and side effects, not implementation details
- Over-mocking to achieve isolation: If everything is mocked, the test validates mocks, not system behavior </common_mistakes>
Resources
Key Principles:
- Test Pyramid concept (Mike Cohn, 2009): more unit tests, fewer integration tests, even fewer E2E
- Arrange-Act-Assert pattern (Bill Wake, 2001) for test structure
- Given-When-Then (Dan North and Chris Matts, ~2006) for behavior specification
See also: Language-specific skills for framework-specific testing patterns (JUnit, pytest, clojure.test, RackUnit, PlUnit).
Footnotes
-
Gerard Meszaros. 2007. xUnit Test Patterns: Refactoring Test Code. Addison-Wesley. </test_doubles> ↩
Berkas dalam skill ini
1Pertanyaan seputar skill test-driven-development
Bagaimana cara install skill test-driven-development di Claude Code?
Jalankan npx degit Pyroxin/opinionated-claude-skills/opinionated-software-engineering/skills/test-driven-development ~/.claude/skills/test-driven-development. Claude Code memuat skill dari ~/.claude/skills (semua proyek) atau .claude/skills (satu proyek) dan menggunakan skill saat deskripsinya cocok dengan tugas.
Client mana saja yang bisa menggunakan skill test-driven-development?
Client apa pun yang membaca format Agent Skills: Claude Code, Codex, Gemini CLI, dan claude.ai (unggah sebagai ZIP). Script dalam skill hanya berjalan di client yang mengizinkan eksekusi kode.
Apa yang harus saya lakukan saat sebuah test gagal?
Anda harus membaca test tersebut untuk memahami expected behavior, melakukan debug pada implementasi untuk mencari penyebab kegagalan, dan memperbaiki implementasinya. Jangan mengubah test hanya agar ia berhasil (pass).
Bagaimana saya harus merespons jika sebuah unit sulit untuk di-test?
Kesulitan dalam melakukan testing adalah tanda dari desain yang buruk; alih-alih mencoba memaksakan penggunaan mocks, Anda sebaiknya melakukan refactor pada kode tersebut.