| name | test-writing |
| description | Defines patterns for writing tests, including subsection tests for incremental development and acceptance criteria verification. Use when writing any tests to ensure consistent naming, structure, and coverage patterns. |
| license | MIT |
| metadata | [object Object] |
Test Writing Pattern
This skill defines how to write tests following established patterns and best practices.
Test Structure
File Organization
- Unit tests:
tests/unit/<module>/test_<component>.py - Integration tests:
tests/integration/test_<feature>.py - API tests:
tests/integration/api/test_api_<endpoint>.py
Test Class Structure
"""Tests for <component>: <description>.
Tests verify:
- <key requirement 1>
- <key requirement 2>
- <key requirement 3>
"""
import pytest
from <appropriate imports>
@pytest.fixture
def <fixture_name>():
"""Fixture description."""
# Setup
yield <value>
# Cleanup (if needed)
class Test<ComponentName>:
"""Test <component>: <description>."""
def test_feature_description(self, fixture) -> None:
"""Test specific feature requirement."""
# Arrange
# Act
# Assert
Naming Conventions
Subsection Tests
For incremental development during phase implementation:
def test_subsection_X_Y_Z_requirement_description(self) -> None:
"""Test subsection X.Y.Z requirement."""
Examples:
test_subsection_1_2_3_validates_input_boundstest_subsection_2_1_1_returns_200_with_healthy_statustest_subsection_3_2_1_enforces_timeout
Acceptance Criteria Tests
For official verification (AC-X.Y.Z format):
def test_ac_X_Y_Z_requirement_description(self) -> None:
"""Test AC-X.Y.Z: requirement description."""
Examples:
test_ac_2_1_1_returns_200_with_health_statustest_ac_3_2_1_response_completes_within_100ms
General Tests
For other test cases:
def test_feature_description(self) -> None:
"""Test that feature works correctly."""
Type Annotations
Always include return type annotations:
def test_something(self) -> None:
"""Test description."""
pass
Test Patterns
Unit Tests
Test individual components in isolation:
def test_component_behavior(self) -> None:
"""Test component does X."""
# Create component
component = Component()
# Call method
result = component.method()
# Assert behavior
assert result.expected_property == expected_value
Integration Tests
Test interactions between components:
@pytest.fixture
def service():
"""Create service for testing."""
return Service()
def test_service_pipeline_integration(self, service) -> None:
"""Test full service pipeline."""
# Set up test data
test_data = create_test_data()
# Run pipeline
result = service.process(test_data)
# Verify result
assert result.success is True
assert result.data is not None
API Tests
Test FastAPI endpoints:
from fastapi.testclient import TestClient
@pytest.fixture
def client():
"""Create test client."""
return TestClient(app)
def test_get_endpoint_returns_200(self, client: TestClient) -> None:
"""Test GET endpoint returns 200."""
response = client.get("/endpoint")
assert response.status_code == 200
data = response.json()
assert data["expected_field"] == expected_value
Assertion Patterns
Success Cases
assert result.success is True
assert result.data is not None
assert result.data.expected_property == expected_value
Error Cases
assert result.success is False
assert result.error_code == "E_ERROR_CODE"
assert result.error_message is not None
Status Codes
assert response.status_code == 200 # Success
assert response.status_code == 400 # Bad Request
assert response.status_code == 404 # Not Found
assert response.status_code == 500 # Internal Error
assert response.status_code == 503 # Service Unavailable
Fixtures
Common Fixtures
@pytest.fixture
def sample_data():
"""Create sample test data."""
return {
"id": "test-id",
"name": "Test Item",
"status": "active"
}
@pytest.fixture
def service():
"""Create service instance."""
return Service()
Autouse Fixtures
For setup/teardown needed for all tests:
@pytest.fixture(autouse=True)
def reset_state():
"""Reset state before each test."""
# Setup
yield
# Cleanup
Test Coverage Requirements
Core Business Logic
- Target: 100% coverage
- Test all validators
- Test all edge cases
- Test model creation and serialization
- Test state transitions
Service Layer
- Target: ≥90% coverage
- Test each service in isolation
- Test service integration
- Test timeout mechanisms
- Test error handling
API Endpoints
- Target: ≥80% coverage
- Test all endpoints
- Test success and error cases
- Test request/response models
- Test error code mapping
UI Components
- Target: ≥70% coverage
- Test component rendering
- Test user interactions
- Test state management
- Test error boundaries
Best Practices
- Descriptive names: Test names should clearly describe what is tested
- One assertion per concept: Group related assertions, but test one concept per test
- Arrange-Act-Assert: Structure tests clearly
- Use fixtures: Avoid code duplication with fixtures
- Test edge cases: Include boundary conditions and error cases
- Test independently: Tests should not depend on execution order
- Verify acceptance criteria: Ensure AC-X.Y.Z requirements are met
Common Edge Cases
Testing Error Codes
def test_returns_correct_error_code(self) -> None:
"""Test returns correct error code."""
result = component.method(invalid_input)
assert result.success is False
assert result.error_code == "E_ERROR_CODE"
Testing Timeouts
def test_enforces_timeout(self) -> None:
"""Test timeout is enforced."""
start_time = time.time()
result = slow_operation()
elapsed_time = time.time() - start_time
assert result.error_code == "E_TIMEOUT"
# Note: Don't assert exact timing, just that timeout occurred
Testing State Changes
def test_updates_state_correctly(self) -> None:
"""Test state is updated correctly."""
initial_state = get_state()
operation()
updated_state = get_state()
assert updated_state.property != initial_state.property