Test-Driven Development
Generate code that passes provided tests, or create tests first then implement.
Workflow
When user provides tests
- Analyze tests: Understand expected inputs, outputs, and edge cases
- Identify requirements: Extract implicit requirements from test assertions
- Implement solution: Write minimal code that passes all tests
- Verify: Run tests to confirm implementation
- Refactor: Improve code while keeping tests green
When user describes behavior
- Clarify requirements: Ask for specific input/output examples
- Write tests first: Create tests covering happy path and edge cases
- Present tests: Show tests to user for confirmation
- Implement: Write code that passes tests
- Iterate: Add tests for additional cases as needed
Prompting for test cases
When user provides vague requirements, ask:
- What inputs does this function accept?
- What should it return for a typical case?
- What edge cases should be handled? (empty input, null, boundaries)
- What errors should be raised and when?
Test template
import pytest
class TestCalculateDiscount:
"""Tests for discount calculation logic."""
def test_no_discount_for_small_order(self):
"""Orders under threshold receive no discount."""
assert calculate_discount(amount=500) == 0
def test_5_percent_discount_over_1000(self):
"""Orders over 1000 receive 5% discount."""
assert calculate_discount(amount=1500) == 75
def test_10_percent_discount_over_5000(self):
"""Orders over 5000 receive 10% discount."""
assert calculate_discount(amount=6000) == 600
def test_zero_amount_returns_zero(self):
"""Zero order amount returns zero discount."""
assert calculate_discount(amount=0) == 0
def test_negative_amount_raises_error(self):
"""Negative amounts are invalid."""
with pytest.raises(ValueError):
calculate_discount(amount=-100)
Implementation workflow
Given the tests above, implement step by step:
def calculate_discount(amount: float) -> float:
"""Calculate order discount based on amount thresholds."""
if amount < 0:
raise ValueError("Amount cannot be negative")
if amount >= 5000:
return amount * 0.10
elif amount >= 1000:
return amount * 0.05
else:
return 0
Running tests
# Run all tests
pytest tests/ -v
# Run specific test file
pytest tests/test_discount.py -v
# Run with coverage
pytest tests/ --cov=src --cov-report=term-missing
Best practices
- One assertion per test (when practical): Makes failures clear
- Descriptive test names:
test_empty_cart_returns_zero_total - Arrange-Act-Assert pattern: Setup, execute, verify
- Test edge cases: Empty, null, boundaries, errors
- Keep tests independent: No shared state between tests
Example interaction
User: "Write a function that validates email addresses"
Response:
- First, write tests defining valid/invalid emails
- Show tests:
test_valid_email,test_missing_at_symbol,test_empty_string - Ask: "Do these test cases cover your requirements?"
- After confirmation, implement the function
- Run tests to verify
