Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Apply Python testing best practices with pytest, fixtures, mocking, and coverage strategies.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
SKILL.md
1---2name: python-testing-patterns3description: Implement comprehensive testing strategies with pytest, fixtures, mocking, and test-driven development. Use when writing Python tests, setting up test suites, or implementing testing best practices.4---56# Python Testing Patterns78Comprehensive guide to implementing robust testing strategies in Python using pytest, fixtures, mocking, parameterization, and test-driven development practices.910## When to Use This Skill1112- Writing unit tests for Python code13- Setting up test suites and test infrastructure14- Implementing test-driven development (TDD)15- Creating integration tests for APIs and services16- Mocking external dependencies and services17- Testing async code and concurrent operations18- Setting up continuous testing in CI/CD19- Implementing property-based testing20- Testing database operations21- Debugging failing tests2223## Core Concepts2425### 1. Test Types2627- **Unit Tests**: Test individual functions/classes in isolation28- **Integration Tests**: Test interaction between components29- **Functional Tests**: Test complete features end-to-end30- **Performance Tests**: Measure speed and resource usage3132### 2. Test Structure (AAA Pattern)3334- **Arrange**: Set up test data and preconditions35- **Act**: Execute the code under test36- **Assert**: Verify the results3738### 3. Test Coverage3940- Measure what code is exercised by tests41- Identify untested code paths42- Aim for meaningful coverage, not just high percentages4344### 4. Test Isolation4546- Tests should be independent47- No shared state between tests48- Each test should clean up after itself4950## Quick Start5152```python53# test_example.py54def add(a, b):55return a + b5657def test_add():58"""Basic test example."""59result = add(2, 3)60assert result == 56162def test_add_negative():63"""Test with negative numbers."""64assert add(-1, 1) == 06566# Run with: pytest test_example.py67```6869## Detailed patterns and worked examples7071Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.7273## Testing Best Practices7475### Test Organization7677```python78# tests/79# __init__.py80# conftest.py # Shared fixtures81# test_unit/ # Unit tests82# test_models.py83# test_utils.py84# test_integration/ # Integration tests85# test_api.py86# test_database.py87# test_e2e/ # End-to-end tests88# test_workflows.py89```9091### Test Naming Convention9293A common pattern: `test_<unit>_<scenario>_<expected_outcome>`. Adapt to your team's preferences.9495```python96# Pattern: test_<unit>_<scenario>_<expected>97def test_create_user_with_valid_data_returns_user():98...99100def test_create_user_with_duplicate_email_raises_conflict():101...102103def test_get_user_with_unknown_id_returns_none():104...105106# Good test names - clear and descriptive107def test_user_creation_with_valid_data():108"""Clear name describes what is being tested."""109pass110111def test_login_fails_with_invalid_password():112"""Name describes expected behavior."""113pass114115def test_api_returns_404_for_missing_resource():116"""Specific about inputs and expected outcomes."""117pass118119# Bad test names - avoid these120def test_1(): # Not descriptive121pass122123def test_user(): # Too vague124pass125126def test_function(): # Doesn't explain what's tested127pass128```129130### Testing Retry Behavior131132Verify that retry logic works correctly using mock side effects.133134```python135from unittest.mock import Mock136137def test_retries_on_transient_error():138"""Test that service retries on transient failures."""139client = Mock()140# Fail twice, then succeed141client.request.side_effect = [142ConnectionError("Failed"),143ConnectionError("Failed"),144{"status": "ok"},145]146147service = ServiceWithRetry(client, max_retries=3)148result = service.fetch()149150assert result == {"status": "ok"}151assert client.request.call_count == 3152153def test_gives_up_after_max_retries():154"""Test that service stops retrying after max attempts."""155client = Mock()156client.request.side_effect = ConnectionError("Failed")157158service = ServiceWithRetry(client, max_retries=3)159160with pytest.raises(ConnectionError):161service.fetch()162163assert client.request.call_count == 3164165def test_does_not_retry_on_permanent_error():166"""Test that permanent errors are not retried."""167client = Mock()168client.request.side_effect = ValueError("Invalid input")169170service = ServiceWithRetry(client, max_retries=3)171172with pytest.raises(ValueError):173service.fetch()174175# Only called once - no retry for ValueError176assert client.request.call_count == 1177```178179### Mocking Time with Freezegun180181Use freezegun to control time in tests for predictable time-dependent behavior.182183```python184from freezegun import freeze_time185from datetime import datetime, timedelta186187@freeze_time("2026-01-15 10:00:00")188def test_token_expiry():189"""Test token expires at correct time."""190token = create_token(expires_in_seconds=3600)191assert token.expires_at == datetime(2026, 1, 15, 11, 0, 0)192193@freeze_time("2026-01-15 10:00:00")194def test_is_expired_returns_false_before_expiry():195"""Test token is not expired when within validity period."""196token = create_token(expires_in_seconds=3600)197assert not token.is_expired()198199@freeze_time("2026-01-15 12:00:00")200def test_is_expired_returns_true_after_expiry():201"""Test token is expired after validity period."""202token = Token(expires_at=datetime(2026, 1, 15, 11, 30, 0))203assert token.is_expired()204205def test_with_time_travel():206"""Test behavior across time using freeze_time context."""207with freeze_time("2026-01-01") as frozen_time:208item = create_item()209assert item.created_at == datetime(2026, 1, 1)210211# Move forward in time212frozen_time.move_to("2026-01-15")213assert item.age_days == 14214```215216### Test Markers217218```python219# test_markers.py220import pytest221222@pytest.mark.slow223def test_slow_operation():224"""Mark slow tests."""225import time226time.sleep(2)227228229@pytest.mark.integration230def test_database_integration():231"""Mark integration tests."""232pass233234235@pytest.mark.skip(reason="Feature not implemented yet")236def test_future_feature():237"""Skip tests temporarily."""238pass239240241@pytest.mark.skipif(os.name == "nt", reason="Unix only test")242def test_unix_specific():243"""Conditional skip."""244pass245246247@pytest.mark.xfail(reason="Known bug #123")248def test_known_bug():249"""Mark expected failures."""250assert False251252253# Run with:254# pytest -m slow # Run only slow tests255# pytest -m "not slow" # Skip slow tests256# pytest -m integration # Run integration tests257```258259### Coverage Reporting260261```bash262# Install coverage263pip install pytest-cov264265# Run tests with coverage266pytest --cov=myapp tests/267268# Generate HTML report269pytest --cov=myapp --cov-report=html tests/270271# Fail if coverage below threshold272pytest --cov=myapp --cov-fail-under=80 tests/273274# Show missing lines275pytest --cov=myapp --cov-report=term-missing tests/276```277278For advanced patterns (async testing, monkeypatching, property-based testing, database testing, CI/CD integration, and configuration), see [references/advanced-patterns.md](references/advanced-patterns.md)279