Developer Experience6 min read

Pytest & Python Testing

Write fast, readable Python tests with pytest - fixtures, parametrize, mocking, coverage, and test organization for reliable software.

PytestTestingPythonFixturesMocking

skill: Pytest & Python Testing category: Developer Experience tags: [Pytest, Testing, Python, Fixtures, Mocking] readingTime: 6

Tests are the safety net that makes refactoring safe. A test that takes 30 seconds to write catches a bug that takes 3 hours to debug in production. Write tests as you code, not after - it's faster and produces better-designed code.

Workflow

  1. Structure tests with pytest conventionsPytest auto-discovers tests matching test_*.py or *_test.py and functions starting with test_. No test class or setUp needed.
  2. Use fixtures for reusable setupFixtures inject test dependencies - database connections, test clients, sample data - with guaranteed setup and teardown.
  3. Parametrize to test multiple casesParametrize runs one test function with multiple inputs - DRY test coverage of edge cases without copy-pasting.
  4. Mock external dependenciesTests that call real APIs, databases, or third-party services are slow and flaky. Mock them to test your code in isolation.
  5. Measure and enforce coverageCoverage shows which code paths are untested - aim for > 80% with focus on business logic, not 100% on every getter.

Output Format

Pytest suite: fixtures in conftest.py, parametrized edge cases, mocked externals, and 80%+ coverage enforced in CI.

tests/
  conftest.py      # shared fixtures (app, db, client)
  test_models.py   # unit tests
  test_views.py    # integration tests
  test_services.py # service layer
pytest --cov --cov-fail-under=80

Examples

Input

Output

Edge Cases & Gotchas

  • Async tests - Async functions need pytest-asyncio: @pytest.mark.asyncio. Add asyncio_mode = "auto" in pytest.ini to avoid decorating every test.
  • Database state leakage - Tests that commit to a real database leave state for subsequent tests. Use transaction rollback fixtures or factory_boy to create isolated test data.
  • Flaky time-dependent tests - Tests that use datetime.now() are flaky. Freeze time with freezegun: @freeze_time("2026-08-05").
  • Slow fixture setup - App/DB setup that runs once per test multiplies build time. Use scope="session" for fixtures that can be shared across all tests.

When to Break These Rules

  • Use unittest for large legacy codebases already written with it - migrating to pytest mid-project is not worth the effort.
  • Use Hypothesis for property-based testing when you want to auto-generate edge case inputs rather than hand-write parametrize values.

Reference Files

  • https://docs.pytest.org/
  • https://pytest-cov.readthedocs.io/

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.