Parametrize, Mocking & Coverage
Parametrize
import pytest
@pytest.mark.parametrize('a,b,expected', [
(1, 2, 3),
(4, 5, 9),
(-1, 1, 0),
])
def test_add(a, b, expected):
assert a + b == expected
# Each tuple becomes a separate reported test case —
# a failure points straight at the exact input that broke.
# Custom, readable test IDs instead of pytest's auto-generated ones
@pytest.mark.parametrize('email,valid', [
('a@b.com', True),
('not-an-email', False),
], ids=['valid-email', 'missing-at-sign'])
def test_email_validation(email, valid):
assert is_valid_email(email) == valid
# Skip/xfail
@pytest.mark.skip(reason='not implemented yet')
def test_future_feature():
...
@pytest.mark.skipif(sys.platform == 'win32', reason='posix-only')
def test_symlinks():
...
@pytest.mark.xfail(reason='known bug, see #123', strict=True)
def test_known_bug():
assert broken_function() == 42Mocking & monkeypatch
# pytest-mock's `mocker` fixture — auto-unpatches after the test
def test_sends_email(mocker):
mock_send = mocker.patch('myapp.email.send')
register_user('alice@example.com')
mock_send.assert_called_once_with('alice@example.com', subject='Welcome')
# Built-in monkeypatch fixture — scoped helpers, auto-restored
def test_reads_api_key(monkeypatch):
monkeypatch.setenv('API_KEY', 'test-key-123')
assert get_api_key() == 'test-key-123'
def test_missing_env_var(monkeypatch):
monkeypatch.delenv('API_KEY', raising=False)
with pytest.raises(RuntimeError):
get_api_key()
# tmp_path — unique temp directory per test, cleaned up automatically
def test_writes_file(tmp_path):
file = tmp_path / 'output.txt'
write_report(file)
assert file.read_text() == 'expected content'
# capsys — capture stdout/stderr; caplog — capture log records
def test_prints_summary(capsys):
print_summary()
captured = capsys.readouterr()
assert 'Done' in captured.outCoverage & CI
pip install pytest-cov pytest-xdist
# Coverage report
pytest --cov=myapp --cov-report=html --cov-report=term-missing
pytest --cov=myapp --cov-fail-under=80 # fail the build below 80%
# Parallel test run across CPUs — tests must be independent!
pytest -n auto
# doctest — run examples embedded in docstrings as real tests
pytest --doctest-modules myapp/
# Typical pyproject.toml config
# [tool.pytest.ini_options]
# testpaths = ["tests"]
# addopts = "-ra -q --strict-markers"
# markers = ["slow: marks slow tests"]Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free