pytest cheatsheet.
Basic test
def test_addition():
assert 1 + 1 == 2
Files: test_*.py or *_test.py. Functions: test_*.
Run
pytest # all
pytest tests/ # specific dir
pytest tests/test_x.py # specific file
pytest tests/test_x.py::test_one # specific test
pytest -k "user and not admin" # by name pattern
pytest -m "slow" # by marker
pytest -x # stop on first failure
pytest --lf # last failed
pytest --ff # failed first
pytest -v # verbose
pytest -s # show print output
pytest -p no:cacheprovider # disable cache
Fixtures
import pytest
@pytest.fixture
def db():
conn = connect()
yield conn
conn.close()
def test_x(db):
db.execute("...")
Setup + yield + teardown.
Fixture scopes
@pytest.fixture(scope="function") # default
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
@pytest.fixture(scope="session") # for whole test run
Session scope: shared across all tests.
conftest.py
# tests/conftest.py
@pytest.fixture
def client():
return TestClient(app)
Available to all tests in the directory + subdirs.
Parametrize
@pytest.mark.parametrize("a, b, expected", [
(1, 2, 3),
(5, 5, 10),
(0, 0, 0),
])
def test_add(a, b, expected):
assert a + b == expected
Multiple test cases from one function.
Parametrize with ids
@pytest.mark.parametrize("payload", [
{"name": "alice"},
{"name": "bob"},
], ids=["alice-test", "bob-test"])
def test_users(payload):
...
Custom test names.
Markers
@pytest.mark.slow
def test_heavy():
...
# Run only slow
pytest -m slow
# Run all except slow
pytest -m "not slow"
Register in pyproject.toml:
[tool.pytest.ini_options]
markers = [
"slow: long-running tests",
"integration: hits a real DB",
]
Skip / xfail
@pytest.mark.skip(reason="bug #123")
def test_broken(): ...
@pytest.mark.skipif(sys.version_info < (3, 12), reason="3.12+ only")
def test_new_feature(): ...
@pytest.mark.xfail
def test_will_fail():
assert False
# Marked as expected failure
Raises
import pytest
def test_division_error():
with pytest.raises(ZeroDivisionError):
1 / 0
def test_msg():
with pytest.raises(ValueError, match="must be positive"):
validate(-1)
pytest.warns
def test_deprecation():
with pytest.warns(DeprecationWarning):
old_function()
Capsys / capfd
def test_print(capsys):
print("hello")
captured = capsys.readouterr()
assert "hello" in captured.out
Tmp paths
def test_tmp(tmp_path):
f = tmp_path / "x.txt"
f.write_text("hi")
assert f.read_text() == "hi"
tmp_path is a pathlib.Path; auto-cleaned.
Monkeypatch
def test_env(monkeypatch):
monkeypatch.setenv("API_KEY", "test")
monkeypatch.setattr("module.func", lambda: "mocked")
monkeypatch.delattr("module.attr", raising=False)
Indirect parametrize
@pytest.fixture
def user(request):
return User(name=request.param)
@pytest.mark.parametrize("user", ["alice", "bob"], indirect=True)
def test_user(user):
...
Async tests (pytest-anyio)
import pytest
@pytest.fixture(scope="session")
def anyio_backend():
return "asyncio"
@pytest.mark.anyio
async def test_async():
result = await fetch()
assert result is not None
Or pytest-asyncio:
@pytest.mark.asyncio
async def test_async(): ...
Approx (float comparisons)
import pytest
def test_floats():
assert 0.1 + 0.2 == pytest.approx(0.3)
assert [1.0, 2.0] == pytest.approx([1.0, 2.0001], rel=1e-3)
Snapshot testing
# syrupy
def test_snapshot(snapshot):
result = compute()
assert result == snapshot
Run once to capture; future runs assert match.
Coverage
pytest --cov=src --cov-report=html --cov-report=term --cov-fail-under=80
Configuration in pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers --cov=src"
markers = ["slow: long-running"]
filterwarnings = ["error", "ignore::DeprecationWarning"]
Common mistakes
- Mutable fixture state shared across tests.
- Forgetting
scope="session"for expensive setup. - Tests depending on order.
time.sleepin tests — flaky; mock time or use polling.- Skipping
@pytest.mark.parametrizefor similar tests.
Read this next
If you want my pytest configuration + plugins setup, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .