Skip to content

Latest commit

 

History

History
146 lines (109 loc) · 2.72 KB

File metadata and controls

146 lines (109 loc) · 2.72 KB

Pytest Guides

This guide covers opinionated defaults and quick how‑tos for a healthy pytest setup.

Conventions

  • Place tests under tests/ and name files test_*.py.
  • Use pytest.ini to centralize options and markers.
  • Prefer small, focused tests; extract setup into fixtures.
  • Treat warnings as errors (except when silenced intentionally).

Config (pytest.ini)

Recommended baseline:

[pytest]
minversion = 7.0
addopts = -ra
testpaths = tests
xfail_strict = true
filterwarnings =
    error::DeprecationWarning
markers =
    unit: fast unit tests
    integration: tests using external services or IO
    slow: long-running tests
    flaky: non-deterministic tests (aim to eliminate)

Essential Fixtures

  • tmp_path: unique temporary directory per test.
  • monkeypatch: patch env vars/attributes; avoid global side effects.
  • caplog, capsys: capture logs/stdout.
  • mocker (pytest-mock): simple, readable mocking.

Example conftest.py:

import os
import random
import pytest

@pytest.fixture(autouse=True)
def _seed_random():
    random.seed(1337)
    os.environ.setdefault("PYTHONHASHSEED", "0")

@pytest.fixture
def tmp_cwd(tmp_path, monkeypatch):
    monkeypatch.chdir(tmp_path)
    return tmp_path

Parametrization

import pytest

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

Async Tests

import asyncio
import pytest

@pytest.mark.asyncio
async def test_async_op():
    await asyncio.sleep(0)
    assert True

CLI Testing

If using argparse or click:

from yourpkg.__main__ import main

def test_cli_smoke(capsys):
    assert main(["--help"]) == 0
    out = capsys.readouterr().out
    assert "Usage" in out or "--help" in out

For Click:

from click.testing import CliRunner
from yourpkg.cli import cli

def test_click_cli():
    result = CliRunner().invoke(cli, ["--version"]) 
    assert result.exit_code == 0

HTTP / FastAPI

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient

@pytest.fixture
def app():
    app = FastAPI()
    @app.get("/ping")
    def ping():
        return {"ok": True}
    return app

def test_ping(app):
    client = TestClient(app)
    r = client.get("/ping")
    assert r.status_code == 200 and r.json()["ok"] is True

Coverage

Run: pytest --cov=yourpkg --cov-report=term-missing

Config in .coveragerc:

[run]
branch = True
source =
    yourpkg

[report]
show_missing = True
skip_covered = True
fail_under = 80

CI

  • Use a matrix for Python versions you support.
  • Cache pip; run pytest -q --cov.
  • If using src/ layout, pip install -e . before running tests.