Testing Python code with pytest — fixtures, parametrization, markers, mocking, coverage, and configuration. Use when writing or running Python tests, setting up a test suite, debugging failing tests, adding fixtures or parametrized cases, measuring coverage, or configuring pytest in pyproject.toml. Pairs with test-driven-development for the workflow/methodology.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add stanfish06/skillquarium --skill pytest --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Pytest?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/stanfish06-pytest)More formats (shields.io, HTML) on the badges page.
---
name: pytest
description: Testing Python code with pytest — fixtures, parametrization, markers, mocking, coverage, and configuration. Use when writing or running Python tests, setting up a test suite, debugging failing tests, adding fixtures or parametrized cases, measuring coverage, or configuring pytest in pyproject.toml. Pairs with test-driven-development for the workflow/methodology.
---
# pytest — Python testing
## Overview
`pytest` is the de-facto Python test framework: plain `assert`, powerful fixtures, and a
rich plugin ecosystem. This skill is the **tool reference**; for the red-green-refactor
*methodology* see [[test-driven-development]].
```bash
uv run pytest # or: pytest (see modern-python for uv)
pytest -q # quiet
pytest -x --ff # stop at first failure, run last-failed first
pytest -k "auth and not slow" # filter by name expression
pytest path/test_mod.py::test_login # one test
pytest -m "not slow" # filter by marker
pytest -vv -s # verbose, don't capture stdout (for debugging)
```
## Test structure & assertions
```python
# test_calc.py — files test_*.py, functions test_*, classes Test*
from calc import add
def test_add():
assert add(2, 3) == 5
def test_divide_by_zero():
import pytest
with pytest.raises(ZeroDivisionError):
1 / 0
```
Use bare `assert` — pytest rewrites it to show rich diffs. For approximate floats use
`pytest.approx(0.1 + 0.2) == 0.3`.
## Fixtures — setup/teardown and shared state
```python
import pytest
@pytest.fixture
def db():
conn = connect(":memory:")
yield conn # everything before yield = setup; after = teardown
conn.close()
def test_query(db): # request the fixture by parameter name
assert db.execute("select 1").fetchone() == (1,)
@pytest.fixture(scope="session") # function (default) | class | module | session
def heavy_model():
return load_model()
```
Put shared fixtures in `conftest.py` (auto-discovered, no import needed). Built-in fixtures
worth knowing: `tmp_path` (per-test temp dir), `monkeypatch` (patch env/attrs/cwd safely),
`capsys` (capture stdout/stderr), `caplog` (capture logging).
## Parametrize — table-driven tests
```python
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(-1, 1, 0),
(0, 0, 0),
])
def test_add(a, b, expected):
assert add(a, b) == expected
```
Each row is a separate reported test. Stack `@parametrize` decorators for a cartesian
product. Use `pytest.param(..., marks=pytest.mark.xfail)` to mark individual rows.
## Mocking
Prefer `monkeypatch` for env/attrs; use `unittest.mock` (or the `pytest-mock` `mocker`
fixture) for call assertions:
```python
def test_fetch(monkeypatch):
monkeypatch.setenv("API_KEY", "test")
monkeypatch.setattr("mymod.requests.get", lambda url, **k: FakeResp(200))
def test_called(mocker): # pytest-mock
spy = mocker.patch("mymod.send_email")
do_signup("a@b.com")
spy.assert_called_once_with("a@b.com")
```
Patch where the name is *used*, not where it's defined (`mymod.requests.get`, not
`requests.get`).
## Config & coverage (pyproject.toml)
```toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra -q --strict-markers"
markers = ["slow: long-running tests", "integration: needs external services"]
[tool.coverage.run]
branch = true
source = ["mypackage"]
```
```bash
uv run pytest --cov=mypackage --cov-report=term-missing
```
`--strict-markers` turns typos in `@pytest.mark.*` into errors — always enable it.
## Gotchas
- **Test isolation:** never rely on test execution order or shared mutable globals; use
fixtures so each test is independent (and `pytest-randomly`/`-p no:randomly` to detect
hidden coupling).
- **`assert` stripped under `-O`:** don't run the suite with `python -O`.
- **async tests** need `pytest-asyncio` (`@pytest.mark.asyncio`) or `anyio`.
- **Slow collection / import errors** usually mean a missing `__init__.py`/`conftest.py`
layout issue or import-time side effects — keep module top-level cheap.
## Related
Methodology in [[test-driven-development]] and [[verification-before-completion]];
property-based testing in [[property-based-testing]]; project setup in [[modern-python]].
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!