con un clic
designing-tests
// Guides test strategy, TDD/BDD approaches, test coverage planning, and testing best practices. Use when designing test suites, improving coverage, or choosing testing approaches.
// Guides test strategy, TDD/BDD approaches, test coverage planning, and testing best practices. Use when designing test suites, improving coverage, or choosing testing approaches.
Guides systematic project analysis, codebase exploration, and architecture pattern recognition. Use when understanding new codebases, onboarding to projects, or investigating system structure.
Guides REST and GraphQL API design, endpoint patterns, request/response schemas, versioning, and API best practices. Use when building APIs, designing endpoints, or reviewing API contracts.
Guides software architecture decisions, design patterns, and system design principles. Use when designing systems, choosing patterns, or making architectural decisions.
Guides git workflows, branching strategies, commit conventions, and version control best practices. Use when managing repositories, creating branches, or handling merges.
Guides performance optimization, profiling techniques, and bottleneck identification. Use when improving application speed, reducing resource usage, or diagnosing performance issues.
CRITICAL skill for executing multiple Task tool calls in a SINGLE message for true parallelism. Essential for efficient multi-task workflows, subagent coordination, and maximizing throughput.
| name | designing-tests |
| description | Guides test strategy, TDD/BDD approaches, test coverage planning, and testing best practices. Use when designing test suites, improving coverage, or choosing testing approaches. |
| license | MIT |
| compatibility | opencode |
| metadata | {"category":"quality","audience":"developers"} |
Strategies and patterns for designing effective, maintainable test suites.
┌─────────┐
│ E2E │ ← Few, slow, expensive
│ Tests │ (Selenium, Playwright)
├─────────┤
│ │
┌──┤ Integr- │ ← Some, medium speed
│ │ ation │ (API tests, DB tests)
│ │ Tests │
│ ├─────────┤
│ │ │
│ │ Unit │ ← Many, fast, cheap
│ │ Tests │ (Pure functions, isolated)
└──┴─────────┘
| Level | Speed | Scope | Quantity | Purpose |
|---|---|---|---|---|
| Unit | ~ms | Single function/class | Many (70-80%) | Logic correctness |
| Integration | ~s | Multiple components | Some (15-20%) | Component interaction |
| E2E | ~10s+ | Full system | Few (5-10%) | User flows work |
┌─────────────────────────────────┐
│ │
▼ │
┌─────────┐ ┌─────────┐ ┌────────┴──┐
│ RED │───▶│ GREEN │───▶│ REFACTOR │
│ Write │ │ Make │ │ Clean │
│ failing │ │ it │ │ up │
│ test │ │ pass │ │ code │
└─────────┘ └─────────┘ └───────────┘
# Step 1: RED - Write failing test
def test_calculate_total_with_discount():
order = Order(items=[Item(price=100)])
order.apply_discount(10) # 10%
assert order.total() == 90
# Step 2: GREEN - Minimal implementation
class Order:
def __init__(self, items):
self.items = items
self.discount = 0
def apply_discount(self, percent):
self.discount = percent
def total(self):
subtotal = sum(i.price for i in self.items)
return subtotal * (100 - self.discount) / 100
# Step 3: REFACTOR - Clean up (if needed)
Feature: Shopping Cart
As a customer
I want to add items to my cart
So that I can purchase them later
Scenario: Add item to empty cart
Given I have an empty cart
When I add a product "Widget" priced at $10
Then my cart should contain 1 item
And my cart total should be $10
Scenario: Apply discount code
Given I have a cart with total $100
When I apply discount code "SAVE10"
Then my cart total should be $90
def test_user_registration():
# Arrange - Set up preconditions
user_data = {"email": "test@example.com", "password": "secure123"}
user_service = UserService(mock_repository)
# Act - Perform the action
result = user_service.register(user_data)
# Assert - Verify the outcome
assert result.success is True
assert result.user.email == "test@example.com"
def test_order_cancellation():
# Given - a confirmed order
order = create_confirmed_order()
# When - the customer cancels it
order.cancel()
# Then - the order is cancelled and refund initiated
assert order.status == "cancelled"
assert order.refund_initiated is True
class UserBuilder:
def __init__(self):
self.email = "default@test.com"
self.name = "Test User"
self.role = "user"
def with_email(self, email):
self.email = email
return self
def with_role(self, role):
self.role = role
return self
def build(self):
return User(email=self.email, name=self.name, role=self.role)
# Usage
admin = UserBuilder().with_role("admin").build()
class TestUsers:
@staticmethod
def admin():
return User(email="admin@test.com", role="admin")
@staticmethod
def customer():
return User(email="customer@test.com", role="customer")
@staticmethod
def guest():
return User(email=None, role="guest")
| Mock | Don't Mock |
|---|---|
| External APIs | Pure business logic |
| Database (for unit tests) | Simple value objects |
| File system | Deterministic functions |
| Time/random | Core domain entities |
| Third-party services | Internal collaborators (usually) |
| Type | Purpose | Example |
|---|---|---|
| Stub | Return canned responses | mock.return_value = 42 |
| Mock | Verify interactions | mock.assert_called_with(...) |
| Spy | Track real calls | Wraps real object, records calls |
| Fake | Simplified implementation | In-memory database |
# Using unittest.mock
from unittest.mock import Mock, patch
def test_send_email_on_registration():
# Arrange
mock_email_service = Mock()
user_service = UserService(email_service=mock_email_service)
# Act
user_service.register({"email": "test@example.com"})
# Assert
mock_email_service.send_welcome_email.assert_called_once_with("test@example.com")
# Using patch decorator
@patch("app.services.EmailService")
def test_with_patch(mock_email_class):
mock_email_class.return_value.send.return_value = True
# Test code...
import pytest
from testcontainers.postgres import PostgresContainer
@pytest.fixture(scope="session")
def database():
with PostgresContainer("postgres:15") as postgres:
yield postgres.get_connection_url()
def test_user_persistence(database):
repo = UserRepository(database)
user = User(email="test@example.com")
repo.save(user)
retrieved = repo.find_by_email("test@example.com")
assert retrieved.email == user.email
def test_create_user_endpoint(client):
response = client.post("/api/users", json={
"email": "new@example.com",
"password": "secure123"
})
assert response.status_code == 201
assert response.json["email"] == "new@example.com"
assert "id" in response.json
class LoginPage:
def __init__(self, page):
self.page = page
self.email_input = page.locator("#email")
self.password_input = page.locator("#password")
self.submit_button = page.locator("button[type=submit]")
def login(self, email, password):
self.email_input.fill(email)
self.password_input.fill(password)
self.submit_button.click()
return DashboardPage(self.page)
# Usage
def test_successful_login(page):
login_page = LoginPage(page)
dashboard = login_page.login("user@example.com", "password")
assert dashboard.welcome_message.is_visible()
| Priority | What | Why |
|---|---|---|
| High | Business logic | Core value |
| High | Edge cases | Where bugs hide |
| High | Error paths | Graceful failures |
| Medium | Integration points | Contract validation |
| Low | UI layout | Brittle, low value |
| Low | Third-party code | Not your responsibility |
| Metric | Target | Notes |
|---|---|---|
| Line coverage | 70-80% | Basic minimum |
| Branch coverage | 60-70% | Catches conditionals |
| Mutation score | 50-70% | Measures test quality |
HIGH VALUE:
✓ Core business logic
✓ Data transformations
✓ Error handling
✓ Security-sensitive code
LOW VALUE:
✗ Getters/setters
✗ Constructor-only classes
✗ Framework boilerplate
✗ Configuration files
| Cause | Solution |
|---|---|
| Timing issues | Use explicit waits, not sleep |
| Shared state | Isolate test data |
| External dependencies | Mock or use containers |
| Race conditions | Add synchronization |
| Date/time | Mock time providers |
| Random data | Seed random generators |
tests/
├── unit/ # Unit tests
│ ├── services/
│ │ └── test_user_service.py
│ └── models/
│ └── test_order.py
├── integration/ # Integration tests
│ ├── api/
│ │ └── test_user_endpoints.py
│ └── repositories/
│ └── test_user_repository.py
├── e2e/ # End-to-end tests
│ └── test_checkout_flow.py
├── fixtures/ # Shared fixtures
│ └── factories.py
└── conftest.py # Pytest configuration
# Pattern: test_[what]_[condition]_[expected]
def test_calculate_total_with_discount_returns_reduced_price():
pass
def test_login_with_invalid_password_raises_auth_error():
pass
def test_order_when_cancelled_sends_refund_notification():
pass
PYRAMID:
Unit (70%) → Integration (20%) → E2E (10%)
TDD CYCLE:
Red → Green → Refactor
PATTERNS:
AAA: Arrange-Act-Assert
Builder: Fluent test data creation
Page Object: E2E abstraction
MOCK WHEN:
External APIs, Database (unit), Time, Random
COVERAGE:
70-80% line, focus on business logic
NAMING:
test_[what]_[condition]_[expected]