pytest-patterns
Modern pytest test structure with fixtures, parametrization, and the AAA pattern. PROACTIVELY activate for: (1) Writing new pytest test suites, (2) Creating test fixtures, (3) Implementing parametrized tests, (4) Testing async code, (5) Organizing tests with conftest.py. Triggers: "pytest", "test", "fixture", "parametrize", "mock", "assert", "conftest", "test coverage"
What this skill does
# Pytest Modern Test Structure
## Core Principles
All Python code in Vibekit **MUST achieve 80%+ test coverage**. This skill provides authoritative patterns for writing clean, maintainable, and effective tests using pytest.
## The AAA Pattern (Arrange-Act-Assert)
Every test should follow the AAA structure:
```python
import pytest
# ✅ REQUIRED: Use AAA pattern for all tests
def test_user_creation():
# ARRANGE: Set up test data and dependencies
user_data = {
"email": "[email protected]",
"username": "testuser",
"age": 25
}
# ACT: Execute the code under test
user = User.create(user_data)
# ASSERT: Verify the outcome
assert user.email == "[email protected]"
assert user.username == "testuser"
assert user.age == 25
assert user.id is not None
# ❌ FORBIDDEN: Tests without clear structure
def bad_test():
user = User.create({"email": "[email protected]", "username": "testuser"})
assert user.email == "[email protected]"
user.update({"age": 30})
assert user.age == 30
# Mixing arrange/act/assert makes tests hard to understand
```
## Fixtures for Test Dependencies
Fixtures provide reusable test setup and teardown:
```python
import pytest
from typing import Generator
# ✅ REQUIRED: Use fixtures for shared setup
@pytest.fixture
def sample_user() -> User:
"""Create a sample user for testing."""
return User(
id=1,
email="[email protected]",
username="testuser"
)
@pytest.fixture
def database_connection() -> Generator:
"""Provide database connection with cleanup."""
# Setup
conn = connect_to_test_db()
yield conn # Provide to test
# Teardown (always runs, even if test fails)
conn.close()
# Using fixtures in tests
def test_user_retrieval(database_connection, sample_user):
# ARRANGE: Dependencies injected via fixtures
db = database_connection
# ACT
retrieved = db.get_user(sample_user.id)
# ASSERT
assert retrieved.id == sample_user.id
assert retrieved.email == sample_user.email
```
## Fixture Scopes
Control fixture lifecycle with scopes:
```python
import pytest
# ✅ REQUIRED: Use narrowest scope possible
@pytest.fixture(scope="function") # Default: runs for each test
def user():
return User(id=1, email="[email protected]")
@pytest.fixture(scope="module") # Runs once per test module
def expensive_setup():
# Expensive operation
return initialize_test_environment()
@pytest.fixture(scope="session") # Runs once per test session
def database_connection():
"""Shared database for all tests."""
conn = connect_to_test_db()
yield conn
conn.close()
# ❌ FORBIDDEN: Using broad scope for mutable fixtures
@pytest.fixture(scope="session") # BAD! Can cause test interdependency
def user_list():
return [] # Mutable state shared across tests - dangerous!
# ✅ GOOD: Use function scope for mutable data
@pytest.fixture(scope="function")
def user_list():
return [] # Fresh list for each test
```
## Parametrized Tests
Test multiple inputs without code duplication:
```python
import pytest
# ✅ REQUIRED: Use parametrize for testing multiple cases
@pytest.mark.parametrize("email,expected_valid", [
("[email protected]", True),
("[email protected]", True),
("invalid-email", False),
("@example.com", False),
("user@", False),
])
def test_email_validation(email: str, expected_valid: bool):
# ARRANGE
validator = EmailValidator()
# ACT
result = validator.is_valid(email)
# ASSERT
assert result == expected_valid
# Multiple parameters
@pytest.mark.parametrize("age,income,expected_eligible", [
(18, 30000, True),
(17, 30000, False),
(18, 20000, False),
(25, 50000, True),
])
def test_loan_eligibility(age: int, income: int, expected_eligible: bool):
assert check_loan_eligibility(age, income) == expected_eligible
```
## Mocking with pytest
Isolate code under test using mocks:
```python
import pytest
from unittest.mock import Mock, patch, MagicMock
# ✅ REQUIRED: Use patch for external dependencies
def test_api_client_with_mock():
# ARRANGE
mock_response = Mock()
mock_response.json.return_value = {"user_id": 123, "name": "Test User"}
mock_response.status_code = 200
with patch('requests.get', return_value=mock_response) as mock_get:
client = APIClient()
# ACT
user = client.get_user(123)
# ASSERT
assert user["user_id"] == 123
assert user["name"] == "Test User"
mock_get.assert_called_once_with("https://api.example.com/users/123")
# Using patch as decorator
@patch('module.expensive_function')
def test_with_decorated_mock(mock_expensive):
# ARRANGE
mock_expensive.return_value = 42
# ACT
result = function_that_calls_expensive()
# ASSERT
assert result == 42
mock_expensive.assert_called_once()
# Mock side effects
def test_retry_logic():
# ARRANGE
mock_api = Mock()
# First call raises, second succeeds
mock_api.fetch.side_effect = [ConnectionError("Network issue"), {"data": "success"}]
# ACT
result = retry_fetch(mock_api)
# ASSERT
assert result == {"data": "success"}
assert mock_api.fetch.call_count == 2
```
## Testing Exceptions
Verify error handling:
```python
import pytest
# ✅ REQUIRED: Use pytest.raises for exception testing
def test_invalid_age_raises_error():
# ARRANGE
invalid_data = {"age": -5}
# ACT & ASSERT
with pytest.raises(ValueError, match="Age must be positive"):
User.create(invalid_data)
# Verifying exception attributes
def test_custom_exception_details():
with pytest.raises(ValidationError) as exc_info:
validate_input({"bad": "data"})
# Assert on exception details
assert exc_info.value.code == "INVALID_INPUT"
assert "bad" in str(exc_info.value)
# ❌ FORBIDDEN: Try/except for expected exceptions
def bad_exception_test():
try:
User.create({"age": -5})
assert False, "Should have raised ValueError"
except ValueError:
pass # Don't do this - use pytest.raises
```
## Async Tests
Test asynchronous code with pytest-asyncio:
```python
import pytest
import asyncio
# ✅ REQUIRED: Use @pytest.mark.asyncio for async tests
@pytest.mark.asyncio
async def test_async_api_call():
# ARRANGE
client = AsyncAPIClient()
# ACT
result = await client.fetch_data("https://api.example.com")
# ASSERT
assert result["status"] == "success"
# Async fixtures
@pytest.fixture
async def async_database():
"""Async fixture with cleanup."""
conn = await connect_async_db()
yield conn
await conn.close()
@pytest.mark.asyncio
async def test_with_async_fixture(async_database):
result = await async_database.query("SELECT * FROM users")
assert len(result) > 0
```
## Test Organization with conftest.py
Share fixtures across multiple test files:
```python
# conftest.py in tests/ directory
import pytest
@pytest.fixture(scope="session")
def database_url():
"""Provide test database URL for all tests."""
return "postgresql://test:test@localhost/test_db"
@pytest.fixture
def sample_user():
"""Sample user available to all test modules."""
return User(id=1, email="[email protected]", username="testuser")
# Tests in any test_*.py file can now use these fixtures
def test_user_creation(sample_user):
assert sample_user.id == 1
```
## Fixture Factories
Create customizable fixtures:
```python
import pytest
# ✅ REQUIRED: Use fixture factories for flexible test data
@pytest.fixture
def user_factory():
"""Factory for creating users with custom attributes."""
def _create_user(**kwargs):
defaults = {
"id": 1,
"email": "[email protected]",
"username": "default_user",
"is_active": True,
}
defaults.update(kwargs)
return User(**defaults)
return _create_user
# Usage
dRelated in Writing & Docs
jax-development
IncludedUse this skill when the user is writing, debugging, profiling, refactoring, reviewing, benchmarking, parallelising, exporting, or explaining JAX code, or when they mention JAX, jax.numpy, jit, grad, value_and_grad, vmap, scan, lax, random keys, pytrees, jax.Array, sharding, Mesh, PartitionSpec, NamedSharding, pmap, shard_map, Pallas, XLA, StableHLO, checkify, profiler, or the JAX repo. It helps turn NumPy or PyTorch-style code into pure functional JAX, fix tracer/control-flow/shape/PRNG bugs, remove recompiles and host-device syncs, choose transforms and sharding strategies, inspect jaxpr/lowering/IR, and benchmark compiled code correctly.
nature-article-writer
IncludedDrafts, rewrites, diagnostically critiques, and style-calibrates primary research manuscripts for Nature and Nature Portfolio journals. Use when the user wants a Nature-style title, summary paragraph or abstract, introduction, results, discussion, methods, figure legends, presubmission enquiry, cover letter, reviewer response, or when a scientific draft sounds generic, jargon-heavy, structurally weak, or AI-ish and needs precise, broad-reader-friendly prose without inventing data, analyses, or references. Best for primary research articles and letters rather than reviews or press releases unless explicitly adapting one.
deckrd
IncludedDocument-driven framework that derives requirements, specifications, implementation plans, and executable tasks from goals through structured AI dialogue. Use when user says "write requirements", "create spec", "plan implementation", "derive tasks", "structure this feature", "break down into tasks", or "document this module". Also use for reverse engineering existing code into docs (/deckrd rev). Do NOT use for direct code writing — use /deckrd-coder after tasks are generated. Do NOT use when the user only wants to run or fix existing code without planning.
clinical-decision-support
IncludedGenerate professional clinical decision support (CDS) documents for pharmaceutical and clinical research settings, including patient cohort analyses (biomarker-stratified with outcomes) and treatment recommendation reports (evidence-based guidelines with decision algorithms). Supports GRADE evidence grading, statistical analysis (hazard ratios, survival curves, waterfall plots), biomarker integration, and regulatory compliance. Outputs publication-ready LaTeX/PDF format optimized for drug development, clinical research, and evidence synthesis.
handling-sf-data
IncludedSalesforce data operations with 130-point scoring. Use this skill to create, update, delete, bulk import/export, generate test data, and clean up org records using sf CLI and anonymous Apex. TRIGGER when: user creates test data, performs bulk import/export, uses sf data CLI commands, needs data factory patterns for Apex tests, or needs to seed/clean records in a Salesforce org. DO NOT TRIGGER when: SOQL query writing only (use querying-soql), Apex test execution (use running-apex-tests), or metadata deployment (use deploying-metadata).
accelint-ac-to-playwright
IncludedConvert and validate acceptance criteria for Playwright test automation. Use when user asks to (1) review/evaluate/check if AC are ready for automation, (2) assess if AC can be converted as-is, (3) validate AC quality for Playwright, (4) turn AC into tests, (5) generate tests from acceptance criteria, (6) convert .md bullets or .feature Gherkin files to Playwright specs, (7) create test automation from requirements. Handles both bullet-style markdown and Gherkin syntax with JSON test plan generation and validation.