Python Testing Patterns
Unverified●29/40Claude Code◐PartialHas SKILL.md but declares no allowed-tools — Claude Code will ask for permission each time
Cursor·UnknownWe have not crawled the repo tree, so we will not guess
Codex·UnknownWe have not crawled the repo tree, so we will not guess
Gemini CLI·UnknownThe spec defines no detection rule for Gemini
Copilot·UnknownWe have not crawled the repo tree, so we will not guess
npx agentalley add python-testing-patternsWho is stuck, and on what
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.
The whole source
Frontmatter — 2 properties
| name | python-testing-patterns |
|---|---|
| description | 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. |
| 1 | --- |
| 2 | name: python-testing-patterns |
| 3 | description: 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 | ---A5 — No allowed-tools declared — no way to tell what this skill may touch |
| 5 | |
| 6 | # Python Testing Patterns |
| 7 | |
| 8 | Comprehensive guide to implementing robust testing strategies in Python using pytest, fixtures, mocking, parameterization, and test-driven development practices. |
| 9 | |
| 10 | ## When to Use This Skill |
| 11 | |
| 12 | - Writing unit tests for Python code |
| 13 | - Setting up test suites and test infrastructure |
| 14 | - Implementing test-driven development (TDD) |
| 15 | - Creating integration tests for APIs and services |
| 16 | - Mocking external dependencies and services |
| 17 | - Testing async code and concurrent operations |
| 18 | - Setting up continuous testing in CI/CD |
| 19 | - Implementing property-based testing |
| 20 | - Testing database operations |
| 21 | - Debugging failing tests |
| 22 | |
| 23 | ## Core Concepts |
| 24 | |
| 25 | ### 1. Test Types |
| 26 | |
| 27 | - **Unit Tests**: Test individual functions/classes in isolation |
| 28 | - **Integration Tests**: Test interaction between components |
| 29 | - **Functional Tests**: Test complete features end-to-end |
| 30 | - **Performance Tests**: Measure speed and resource usage |
| 31 | |
| 32 | ### 2. Test Structure (AAA Pattern) |
| 33 | |
| 34 | - **Arrange**: Set up test data and preconditions |
| 35 | - **Act**: Execute the code under test |
| 36 | - **Assert**: Verify the results |
| 37 | |
| 38 | ### 3. Test Coverage |
| 39 | |
| 40 | - Measure what code is exercised by tests |
| 41 | - Identify untested code paths |
| 42 | - Aim for meaningful coverage, not just high percentages |
| 43 | |
| 44 | ### 4. Test Isolation |
| 45 | |
| 46 | - Tests should be independent |
| 47 | - No shared state between tests |
| 48 | - Each test should clean up after itself |
| 49 | |
| 50 | ## Quick Start |
| 51 | |
| 52 | ```python |
| 53 | # test_example.py |
| 54 | def add(a, b): |
| 55 | return a + b |
| 56 | |
| 57 | def test_add(): |
| 58 | """Basic test example.""" |
| 59 | result = add(2, 3) |
| 60 | assert result == 5 |
| 61 | |
| 62 | def test_add_negative(): |
| 63 | """Test with negative numbers.""" |
| 64 | assert add(-1, 1) == 0 |
| 65 | |
| 66 | # Run with: pytest test_example.py |
| 67 | ``` |
| 68 | |
| 69 | ## Detailed patterns and worked examples |
| 70 | |
| 71 | Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient. |
| 72 | |
| 73 | ## Testing Best Practices |
| 74 | |
| 75 | ### Test Organization |
| 76 | |
| 77 | ```python |
| 78 | # tests/ |
| 79 | # __init__.py |
| 80 | # conftest.py # Shared fixtures |
| 81 | # test_unit/ # Unit tests |
| 82 | # test_models.py |
| 83 | # test_utils.py |
| 84 | # test_integration/ # Integration tests |
| 85 | # test_api.py |
| 86 | # test_database.py |
| 87 | # test_e2e/ # End-to-end tests |
| 88 | # test_workflows.py |
| 89 | ``` |
| 90 | |
| 91 | ### Test Naming Convention |
| 92 | |
| 93 | A common pattern: `test_<unit>_<scenario>_<expected_outcome>`. Adapt to your team's preferences. |
| 94 | |
| 95 | ```python |
| 96 | # Pattern: test_<unit>_<scenario>_<expected> |
| 97 | def test_create_user_with_valid_data_returns_user(): |
| 98 | ... |
| 99 | |
| 100 | def test_create_user_with_duplicate_email_raises_conflict(): |
| 101 | ... |
| 102 | |
| 103 | def test_get_user_with_unknown_id_returns_none(): |
| 104 | ... |
| 105 | |
| 106 | # Good test names - clear and descriptive |
| 107 | def test_user_creation_with_valid_data(): |
| 108 | """Clear name describes what is being tested.""" |
| 109 | pass |
| 110 | |
| 111 | def test_login_fails_with_invalid_password(): |
| 112 | """Name describes expected behavior.""" |
| 113 | pass |
| 114 | |
| 115 | def test_api_returns_404_for_missing_resource(): |
| 116 | """Specific about inputs and expected outcomes.""" |
| 117 | pass |
| 118 | |
| 119 | # Bad test names - avoid these |
| 120 | def test_1(): # Not descriptive |
| 121 | pass |
| 122 | |
| 123 | def test_user(): # Too vague |
| 124 | pass |
| 125 | |
| 126 | def test_function(): # Doesn't explain what's tested |
| 127 | pass |
| 128 | ``` |
| 129 | |
| 130 | ### Testing Retry Behavior |
| 131 | |
| 132 | Verify that retry logic works correctly using mock side effects. |
| 133 | |
| 134 | ```python |
| 135 | from unittest.mock import Mock |
| 136 | |
| 137 | def test_retries_on_transient_error(): |
| 138 | """Test that service retries on transient failures.""" |
| 139 | client = Mock() |
| 140 | # Fail twice, then succeed |
| 141 | client.request.side_effect = [ |
| 142 | ConnectionError("Failed"), |
| 143 | ConnectionError("Failed"), |
| 144 | {"status": "ok"}, |
| 145 | ] |
| 146 | |
| 147 | service = ServiceWithRetry(client, max_retries=3) |
| 148 | result = service.fetch()A4 — This skill pulls in web or user content but never says to treat that content as data. A signal, not proof. |
| 149 | |
| 150 | assert result == {"status": "ok"} |
| 151 | assert client.request.call_count == 3 |
| 152 | |
| 153 | def test_gives_up_after_max_retries(): |
| 154 | """Test that service stops retrying after max attempts.""" |
| 155 | client = Mock() |
| 156 | client.request.side_effect = ConnectionError("Failed") |
| 157 | |
| 158 | service = ServiceWithRetry(client, max_retries=3) |
| 159 | |
| 160 | with pytest.raises(ConnectionError): |
| 161 | service.fetch() |
| 162 | |
| 163 | assert client.request.call_count == 3 |
| 164 | |
| 165 | def test_does_not_retry_on_permanent_error(): |
| 166 | """Test that permanent errors are not retried.""" |
| 167 | client = Mock() |
| 168 | client.request.side_effect = ValueError("Invalid input") |
| 169 | |
| 170 | service = ServiceWithRetry(client, max_retries=3) |
| 171 | |
| 172 | with pytest.raises(ValueError): |
| 173 | service.fetch() |
| 174 | |
| 175 | # Only called once - no retry for ValueError |
| 176 | assert client.request.call_count == 1 |
| 177 | ``` |
| 178 | |
| 179 | ### Mocking Time with Freezegun |
| 180 | |
| 181 | Use freezegun to control time in tests for predictable time-dependent behavior. |
| 182 | |
| 183 | ```python |
| 184 | from freezegun import freeze_time |
| 185 | from datetime import datetime, timedelta |
| 186 | |
| 187 | @freeze_time("2026-01-15 10:00:00") |
| 188 | def test_token_expiry(): |
| 189 | """Test token expires at correct time.""" |
| 190 | token = create_token(expires_in_seconds=3600) |
| 191 | assert token.expires_at == datetime(2026, 1, 15, 11, 0, 0) |
| 192 | |
| 193 | @freeze_time("2026-01-15 10:00:00") |
| 194 | def test_is_expired_returns_false_before_expiry(): |
| 195 | """Test token is not expired when within validity period.""" |
| 196 | token = create_token(expires_in_seconds=3600) |
| 197 | assert not token.is_expired() |
| 198 | |
| 199 | @freeze_time("2026-01-15 12:00:00") |
| 200 | def test_is_expired_returns_true_after_expiry(): |
| 201 | """Test token is expired after validity period.""" |
| 202 | token = Token(expires_at=datetime(2026, 1, 15, 11, 30, 0)) |
| 203 | assert token.is_expired() |
| 204 | |
| 205 | def test_with_time_travel(): |
| 206 | """Test behavior across time using freeze_time context.""" |
| 207 | with freeze_time("2026-01-01") as frozen_time: |
| 208 | item = create_item() |
| 209 | assert item.created_at == datetime(2026, 1, 1) |
| 210 | |
| 211 | # Move forward in time |
| 212 | frozen_time.move_to("2026-01-15") |
| 213 | assert item.age_days == 14 |
| 214 | ``` |
| 215 | |
| 216 | ### Test Markers |
| 217 | |
| 218 | ```python |
| 219 | # test_markers.py |
| 220 | import pytest |
| 221 | |
| 222 | @pytest.mark.slow |
| 223 | def test_slow_operation(): |
| 224 | """Mark slow tests.""" |
| 225 | import time |
| 226 | time.sleep(2) |
| 227 | |
| 228 | |
| 229 | @pytest.mark.integration |
| 230 | def test_database_integration(): |
| 231 | """Mark integration tests.""" |
| 232 | pass |
| 233 | |
| 234 | |
| 235 | @pytest.mark.skip(reason="Feature not implemented yet") |
| 236 | def test_future_feature(): |
| 237 | """Skip tests temporarily.""" |
| 238 | pass |
| 239 | |
| 240 | |
| 241 | @pytest.mark.skipif(os.name == "nt", reason="Unix only test") |
| 242 | def test_unix_specific(): |
| 243 | """Conditional skip.""" |
| 244 | pass |
| 245 | |
| 246 | |
| 247 | @pytest.mark.xfail(reason="Known bug #123") |
| 248 | def test_known_bug(): |
| 249 | """Mark expected failures.""" |
| 250 | assert False |
| 251 | |
| 252 | |
| 253 | # Run with: |
| 254 | # pytest -m slow # Run only slow tests |
| 255 | # pytest -m "not slow" # Skip slow tests |
| 256 | # pytest -m integration # Run integration tests |
| 257 | ``` |
| 258 | |
| 259 | ### Coverage Reporting |
| 260 | |
| 261 | ```bash |
| 262 | # Install coverage |
| 263 | pip install pytest-cov |
| 264 | |
| 265 | # Run tests with coverage |
| 266 | pytest --cov=myapp tests/ |
| 267 | |
| 268 | # Generate HTML report |
| 269 | pytest --cov=myapp --cov-report=html tests/ |
| 270 | |
| 271 | # Fail if coverage below threshold |
| 272 | pytest --cov=myapp --cov-fail-under=80 tests/ |
| 273 | |
| 274 | # Show missing lines |
| 275 | pytest --cov=myapp --cov-report=term-missing tests/ |
| 276 | ``` |
| 277 | |
| 278 | For 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 |
Reviews
Installed this one?Write the first review and take the Trailblazer badge.
Alternatives
Subagent Driven DevelopmentUse when executing implementation plans with independent tasks in the current session◐◐◐◐◐●36/40Python Code Style & DocumentationPython code style, linting, formatting, naming conventions, and documentation standards. Use when writing new code, reviewing style, configuring linters, writing docstrings, or establishing project standards.◐····●35/40Competitor Price Analysis 💲Competitor pricing strategy analysis and market positioning. Price mapping, pricing gaps identification, elasticity signals evaluation, and strategic pricing optimization. Use when the user asks about competitor pricing, price analysis, pricing strategy, or co◐····●34/40Competitor Price Tracker 📊Set up competitor price tracking and monitoring workflows. Track price changes, detect promotions, analyze pricing patterns, and get alerts for competitive price movements.◐····●34/40