Pytest & Python Testing
Write fast, readable Python tests with pytest - fixtures, parametrize, mocking, coverage, and test organization for reliable software.
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
- 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.
- Use fixtures for reusable setupFixtures inject test dependencies - database connections, test clients, sample data - with guaranteed setup and teardown.
- Parametrize to test multiple casesParametrize runs one test function with multiple inputs - DRY test coverage of edge cases without copy-pasting.
- Mock external dependenciesTests that call real APIs, databases, or third-party services are slow and flaky. Mock them to test your code in isolation.
- 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=80Examples
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/