Comprehensive guide for Test-Driven Development (TDD) methodology. Use this skill when the user asks to implement features using TDD, write tests first, follow red-green-refactor cycle, or develop code with test-first approach. Also use when user mentions TDD, unit testing workflow, or wants to refactor code with test coverage.
Provides comprehensive TDD guidance for implementing features using the red-green-refactor cycle. Use when users request test-first development, mention TDD, or want to write tests before code.
/plugin marketplace add chuyeow/human-general-intelligence/plugin install human-general-intelligence@human-general-intelligenceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Test-Driven Development is a software development approach where tests are written before the actual code. This skill guides you through the TDD process, from writing your first failing test to refactoring production code.
The fundamental TDD cycle consists of three phases:
This cycle repeats for each new piece of functionality.
Follow these steps for each feature or behavior you implement:
Before writing any code, enumerate the expected behaviors and edge cases:
Example scenarios for a calculator's add function:
Select one scenario from your list and write an automated test that would pass if that behavior is correctly implemented.
Key principles:
Example test structure:
def test_add_positive_numbers():
# Setup: Arrange the test state
calculator = Calculator()
# Execution: Act on the unit under test
result = calculator.add(2, 3)
# Validation: Assert the expected outcome
assert result == 5
# Cleanup: (if needed) restore state
Execute your entire test suite. The new test must fail for the right reason (not due to syntax errors or missing imports).
Why this matters:
What "fail for the right reason" means:
Implement just enough code to make the failing test pass. Nothing more.
Example:
# First test: add(2, 3) should return 5
def add(a, b):
return 5 # Hard-coded - perfectly acceptable at this stage!
# After more tests, evolve to:
def add(a, b):
return a + b
This "fake it till you make it" approach is intentional and valuable.
Execute the full test suite again. All tests, including the new one, must pass.
If tests fail:
Now improve the code quality without changing behavior. Run tests after each refactoring step to ensure nothing breaks.
Remove duplication, extract methods, improve naming, eliminate hard-coded test data, and simplify logic.
Return to step 1 or 2 with the next scenario on your list. Continue until all scenarios are implemented and tested.
Structure each test consistently for clarity and maintainability:
test_add_returns_sum_of_two_positive_integers is better than test_addWrite the simplest code that makes tests pass. Complexity emerges from refactoring, not from initial implementation.
Only implement features that are needed right now for passing tests. Don't add functionality for hypothetical future requirements.
Test behavior through public interfaces, not internal implementation. This allows refactoring without breaking tests.
Example:
# ❌ Bad: Testing implementation details
def test_internal_cache_structure():
obj = MyClass()
assert obj._cache == {} # Fragile - breaks if implementation changes
# ✅ Good: Testing behavior
def test_caching_improves_performance():
obj = MyClass()
first_call_time = time_function_call(obj.get_data)
second_call_time = time_function_call(obj.get_data)
assert second_call_time < first_call_time * 0.1
When code depends on external systems (databases, APIs, file systems), use test doubles to maintain fast, isolated unit tests:
Test doubles don't prove real connections work. Write integration tests separately to verify actual component interactions, but run them less frequently than unit tests.
❌ Tests that rely on execution order or state from other tests
Why it's bad: Creates fragile test suites where one failure cascades into many false negatives
Solution: Make each test independent with its own setup and teardown
❌ Tests that verify internal structure rather than external behavior
Why it's bad: Tests break whenever you refactor, even if behavior is unchanged
Solution: Test only through public interfaces
❌ Tests that take seconds or minutes to run
Why it's bad: Developers won't run them frequently, defeating TDD's rapid feedback loop
Solution: Use test doubles for external dependencies; move slow tests to separate integration suite
❌ Tests that verify every possible detail of output
Why it's bad: Expensive to maintain, brittle, and time-consuming
Solution: Verify only the aspects relevant to the behavior being tested
❌ Leaving test-specific values in production code after making tests pass
Why it's bad: Production code becomes polluted with test concerns
Solution: Refactor during the "Refactor" phase to remove test data
❌ Tests that assert exact execution times or timestamps
Why it's bad: Flaky tests that fail randomly based on system load
Solution: Allow tolerance ranges (e.g., 5-10% margin) or test relative timing
Let's implement a UserValidator class using TDD:
# Test (Red)
def test_empty_email_is_invalid():
validator = UserValidator()
assert validator.is_valid_email("") == False
# Implementation (Green)
class UserValidator:
def is_valid_email(self, email):
return False # Simplest implementation
# Refactor
# Nothing to refactor yet
# ✅ Test passes
# Test (Red)
def test_valid_email_is_valid():
validator = UserValidator()
assert validator.is_valid_email("user@example.com") == True
# Implementation (Green)
class UserValidator:
def is_valid_email(self, email):
return "@" in email and len(email) > 0 # Simple implementation
# Refactor
class UserValidator:
def is_valid_email(self, email):
if not email:
return False
return "@" in email
# ✅ Both tests pass
# Test (Red)
def test_email_without_domain_is_invalid():
validator = UserValidator()
assert validator.is_valid_email("user@") == False
# Implementation (Green)
import re
class UserValidator:
def is_valid_email(self, email):
if not email:
return False
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
# Refactor
# Extract pattern to class constant
class UserValidator:
EMAIL_PATTERN = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
def is_valid_email(self, email):
if not email:
return False
return bool(re.match(self.EMAIL_PATTERN, email))
# ✅ All three tests pass
Continue this cycle for remaining scenarios (special characters, maximum length, etc.).
Before starting TDD session:
For each scenario:
Common reminders:
Problem: Test is always passing even before implementation
Problem: Can't get test to pass
Problem: Tests are brittle and break often
Problem: Tests are slow
Problem: Lost in refactoring
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.