codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
EDU›Testing strategy and quality gates›Step 3

Step 3

pytest · fixtures · parametrize

0 views

pytest · fixtures · parametrize

The de-facto Python test runner. Terser and more powerful than unittest.

1. Install

uv add --dev pytest pytest-asyncio pytest-cov
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
addopts = "-v --tb=short"

2. First test

def test_addition():
    assert 1 + 1 == 2

3. parametrize

import pytest

@pytest.mark.parametrize("a, b, expected", [
    (1, 1, 2),
    (2, 3, 5),
    (-1, 1, 0),
])
def test_add(a, b, expected):
    assert a + b == expected

4. Fixtures

# tests/conftest.py
import pytest
from httpx import AsyncClient

@pytest.fixture
async def client():
    from main import app
    async with AsyncClient(app=app, base_url="http://test") as c:
        yield c
async def test_create_user(client):
    r = await client.post("/users", json={"email": "x"})
    assert r.status_code == 201

5. Fixture scope

@pytest.fixture(scope="session")
def db_engine(): ...

@pytest.fixture(scope="function")
def db_session(db_engine): ...

Expensive resources in session, mutable state in function.

6. Async tests

import pytest
pytestmark = pytest.mark.asyncio

async def test_async_fn():
    result = await async_compute()
    assert result == 42

7. mocker — pytest-mock

def test_send_email(mocker):
    mock = mocker.patch("app.mail.send_mail")
    process_signup("user@example.com")
    mock.assert_called_once_with("user@example.com", subject=mocker.ANY)

8. Coverage

uv run pytest --cov=app --cov-report=term-missing --cov-report=html

9. Gotchas

  • Fixture name mismatch with parameter
  • Mutating a session-scope fixture contaminates later tests
  • Running async tests sync — configure asyncio_mode or use the marker
  • Circular fixtures

10. Fast runs

pytest -x                  # stop on first failure
pytest -k "test_user"      # name filter
pytest --lf                # rerun last failed
pytest -n auto             # parallel (pytest-xdist)

Closing

Fixtures + parametrize are what pull pytest ahead of vitest for backend work.

Next

  • 04-testcontainers

← Step 2

vitest basics + mock patterns

Step 4 →

testcontainers