Generates and improves tests following TDD principles. Activates when new features are implemented, test coverage is low, or user requests tests. Ensures comprehensive test coverage with unit, integration, and edge case tests.
You are activating test generation and improvement capabilities. Your role is to ensure code has comprehensive, maintainable test coverage.
This skill activates when:
/\
/ \ E2E Tests (Few)
/ \ - Test full workflows
/------\
/ \ Integration Tests (Some)
/ \- Test module interactions
/------------\
/ \ Unit Tests (Many)
/______________/ - Test individual functions
Understand what to test:
Follow this structure:
def test_function_name_condition_expected():
"""
Test that function_name handles condition and returns expected result.
"""
# Arrange: Set up test data and conditions
input_data = ...
expected_output = ...
# Act: Execute the code under test
result = function_name(input_data)
# Assert: Verify the result
assert result == expected_output
Ensure comprehensive coverage:
Test individual functions in isolation.
Python Example:
import pytest
from mymodule import calculate_total
def test_calculate_total_with_valid_items():
"""Calculate total for valid items."""
items = [
{"price": 10.00, "quantity": 2},
{"price": 5.00, "quantity": 3},
]
assert calculate_total(items) == 35.00
def test_calculate_total_with_empty_list():
"""Calculate total returns 0 for empty list."""
assert calculate_total([]) == 0.00
def test_calculate_total_with_zero_quantity():
"""Calculate total handles zero quantity."""
items = [{"price": 10.00, "quantity": 0}]
assert calculate_total(items) == 0.00
def test_calculate_total_raises_on_negative_price():
"""Calculate total raises ValueError for negative price."""
items = [{"price": -10.00, "quantity": 1}]
with pytest.raises(ValueError, match="Price cannot be negative"):
calculate_total(items)
JavaScript Example:
import { describe, it, expect } from "vitest";
import { calculateTotal } from "./cart";
describe("calculateTotal", () => {
it("calculates total for valid items", () => {
const items = [
{ price: 10.0, quantity: 2 },
{ price: 5.0, quantity: 3 },
];
expect(calculateTotal(items)).toBe(35.0);
});
it("returns 0 for empty list", () => {
expect(calculateTotal([])).toBe(0);
});
it("handles zero quantity", () => {
const items = [{ price: 10.0, quantity: 0 }];
expect(calculateTotal(items)).toBe(0);
});
it("throws error for negative price", () => {
const items = [{ price: -10.0, quantity: 1 }];
expect(() => calculateTotal(items)).toThrow("Price cannot be negative");
});
});
Test multiple components working together.
Example:
def test_user_registration_flow():
"""Test complete user registration workflow."""
# Arrange: Set up test database
db = setup_test_database()
api = UserAPI(db)
# Act: Register user
response = api.register({
"email": "test@example.com",
"password": "SecurePass123!",
})
# Assert: User created and email sent
assert response.status == 201
assert db.users.find_by_email("test@example.com") is not None
assert email_was_sent("test@example.com", "Welcome")
Isolate code from external dependencies.
Python Example:
from unittest.mock import Mock, patch
def test_send_notification_calls_email_service():
"""Test that send_notification calls email service correctly."""
# Arrange: Mock email service
mock_email = Mock()
# Act: Send notification
with patch('mymodule.email_service', mock_email):
send_notification("user@example.com", "Hello")
# Assert: Email service called with correct args
mock_email.send.assert_called_once_with(
to="user@example.com",
subject="Notification",
body="Hello"
)
Test multiple inputs efficiently.
Python Example:
@pytest.mark.parametrize("input,expected", [
(0, "zero"),
(1, "one"),
(2, "two"),
(10, "many"),
(-5, "negative"),
])
def test_number_to_word(input, expected):
"""Test number_to_word for various inputs."""
assert number_to_word(input) == expected
Generate test cases automatically.
Python Example:
from hypothesis import given
from hypothesis.strategies import integers
@given(integers())
def test_double_is_reversible(x):
"""Test that doubling and halving returns original value."""
assert half(double(x)) == x
project/
├── src/
│ └── mymodule/
│ ├── __init__.py
│ ├── calculator.py
│ └── user.py
└── tests/
├── __init__.py
├── unit/
│ ├── test_calculator.py
│ └── test_user.py
├── integration/
│ └── test_user_flow.py
└── conftest.py # Shared fixtures
test_*.py or *_test.pytest_<function>_<condition>_<expected>Test<ClassName>Good Names:
test_calculate_total_with_empty_list_returns_zerotest_user_login_with_invalid_password_raises_errortest_api_returns_404_for_nonexistent_resourceBad Names:
test_calc (too vague)test_1 (no meaning)test_it_works (what works?)For each function/class, ensure tests for:
Fast:
Independent:
Repeatable:
Self-Validating:
Timely:
Testing Implementation Details:
# Bad: Tests internal state
def test_cache_uses_dict():
cache = Cache()
assert isinstance(cache._storage, dict)
# Good: Tests behavior
def test_cache_stores_and_retrieves_value():
cache = Cache()
cache.set("key", "value")
assert cache.get("key") == "value"
Overly Complex Tests:
# Bad: Test is hard to understand
def test_complex():
data = [process(x) for x in range(100) if x % 2]
result = calculate(data)
assert result == sum(data) / len(data)
# Good: Clear and simple
def test_calculate_returns_average():
data = [2, 4, 6]
assert calculate(data) == 4.0
Not Testing Edge Cases:
# Incomplete: Only tests happy path
def test_divide():
assert divide(10, 2) == 5
# Complete: Tests edge cases too
def test_divide_with_valid_numbers():
assert divide(10, 2) == 5
def test_divide_by_zero_raises_error():
with pytest.raises(ZeroDivisionError):
divide(10, 0)
def test_divide_with_negative_numbers():
assert divide(-10, 2) == -5
~/.amplihack/.claude/agents/tester.mdUser: "I just implemented this authentication function. Can you add tests?"
Testing Code Skill:
Let me analyze the authentication function and create comprehensive tests.
## Function Analysis
The `authenticate(username, password)` function:
- Returns JWT token on success
- Raises `InvalidCredentialsError` on bad password
- Raises `UserNotFoundError` if user doesn't exist
- Hashes password before comparison
- Logs failed attempts
## Test Plan
I'll create tests for:
1. Successful authentication (happy path)
2. Invalid password (error case)
3. Non-existent user (error case)
4. Empty username/password (edge case)
5. SQL injection attempt (security)
6. Logging of failed attempts (side effect)
## Generated Tests
```python
import pytest
from unittest.mock import Mock, patch
from myapp.auth import authenticate, InvalidCredentialsError, UserNotFoundError
class TestAuthenticate:
"""Tests for authenticate function."""
def test_successful_authentication_returns_jwt_token(self, mock_db):
"""Authenticate returns valid JWT for correct credentials."""
# Arrange
mock_db.get_user.return_value = User(
username="alice",
password_hash="$2b$12$..."
)
# Act
token = authenticate("alice", "correct_password")
# Assert
assert token is not None
assert jwt.decode(token)["username"] == "alice"
def test_invalid_password_raises_error(self, mock_db):
"""Authenticate raises InvalidCredentialsError for wrong password."""
mock_db.get_user.return_value = User(
username="alice",
password_hash="$2b$12$..."
)
with pytest.raises(InvalidCredentialsError):
authenticate("alice", "wrong_password")
def test_nonexistent_user_raises_error(self, mock_db):
"""Authenticate raises UserNotFoundError for unknown user."""
mock_db.get_user.return_value = None
with pytest.raises(UserNotFoundError):
authenticate("nobody", "password")
def test_empty_username_raises_error(self):
"""Authenticate raises ValueError for empty username."""
with pytest.raises(ValueError, match="Username cannot be empty"):
authenticate("", "password")
def test_logs_failed_authentication_attempts(self, mock_db, mock_logger):
"""Failed authentication attempts are logged."""
mock_db.get_user.return_value = User(username="alice", ...)
try:
authenticate("alice", "wrong_password")
except InvalidCredentialsError:
pass
mock_logger.warning.assert_called_with(
"Failed login attempt for user: alice"
)
@pytest.fixture
def mock_db():
"""Mock database for tests."""
return Mock()
@pytest.fixture
def mock_logger():
"""Mock logger for tests."""
with patch('myapp.auth.logger') as mock:
yield mock
Running tests:
pytest tests/test_auth.py --cov=myapp.auth --cov-report=term-missing
Current coverage: 95% (missing: edge case for special characters)
Consider adding:
Ready to implement these tests?
## Success Criteria
Good test suite:
- All critical paths covered
- Edge cases tested
- Tests are fast (<1s per file)
- Tests are independent
- Clear test names
- Failures are descriptive
- Easy to add new tests
## Related Capabilities
- **Agent**: Tester agent (this skill invokes it)
- **Skill**: "Reviewing Code" for test quality review
- **Skill**: "Debugging Issues" when tests fail
- **Documentation**: Language-specific testing guides
---
Remember: Tests are documentation that proves your code works. Write tests you'll thank yourself for later.
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer