Sổ tay kỹ thuật

pytest – lệnh và pattern hay dùng

cheatsheet#python#pytest#testing· cập nhật 2026-11-06

Tra nhanh cách chạy chọn lọc test, viết fixture, parametrize, mark, dùng tmp_path/monkeypatch và đo coverage với pytest.

Sửa trang này
Mục lục

Đã chạy thử với pytest 9.0, pytest-cov và pytest-xdist. Cài vào project: uv add --dev pytest pytest-cov, chạy bằng uv run pytest (xem uv cheatsheet). Test FastAPI bằng TestClient xem cấu trúc project FastAPI.

Chạy chọn lọc

Mục đích Lệnh
Chạy gọn / chi tiết từng test pytest -q / pytest -v
Một file / một test / một case parametrize pytest tests/test_a.py / pytest tests/test_a.py::test_parse / pytest "tests/test_a.py::test_parse[bad]"
Lọc theo tên (biểu thức) pytest -k "parse and not bad"
Lọc theo mark pytest -m "not slow"
Dừng ở lỗi đầu tiên / sau N lỗi pytest -x / pytest --maxfail=3
Chỉ chạy lại test fail lần trước pytest --lf (fail trước rồi chạy hết: --ff)
Dừng ở lỗi, lần sau chạy tiếp từ đó pytest --sw (stepwise)
Liệt kê test, không chạy pytest --co -q
Hiện print() ra màn hình pytest -s
Vào debugger khi fail pytest --pdb
Traceback ngắn, kèm biến local pytest --tb=short -l
Tóm tắt lý do skip/xfail/fail cuối output pytest -ra
10 test chậm nhất pytest --durations=10
Chạy song song (pytest-xdist) pytest -n auto

Fixture

tests/conftest.py
import pytest
 
@pytest.fixture(scope="session")      # tạo 1 lần cho cả phiên test
def inventory():
    return {"pe1": "198.51.100.11"}
 
@pytest.fixture                         # mặc định scope="function"
def conn():
    c = {"open": True}                  # setup
    yield c                             # giá trị trả cho test
    c["open"] = False                   # teardown, chạy cả khi test fail
Mục đích Cách làm
Dùng fixture Khai báo tên làm tham số: def test_x(inventory, conn):
Dùng chung cho nhiều file Đặt trong conftest.py (cùng thư mục hoặc thư mục cha)
Phạm vi scope="function" | "class" | "module" | "package" | "session"
Tự áp cho mọi test @pytest.fixture(autouse=True)
Liệt kê fixture có sẵn pytest --fixtures
Xem thứ tự setup/teardown pytest --setup-show -k test_x

Parametrize, mark, assert

tests/test_parse.py
import pytest
 
def parse_vlan(s: str) -> int:
    v = int(s)
    if not 1 <= v <= 4094:
        raise ValueError(f"vlan out of range: {v}")
    return v
 
@pytest.mark.parametrize(
    ("raw", "expected"),
    [("1", 1), ("4094", 4094), pytest.param("4095", None, marks=pytest.mark.xfail(raises=ValueError), id="too-big")],
)
def test_parse_vlan(raw, expected):
    assert parse_vlan(raw) == expected
 
def test_parse_vlan_error():
    with pytest.raises(ValueError, match="out of range"):
        parse_vlan("0")
Mục đích Cú pháp
Bỏ qua có điều kiện @pytest.mark.skipif(os.getenv("LAB") is None, reason="cần LAB=1")
Bỏ qua trong thân test pytest.skip("chưa hỗ trợ junos")
Bỏ qua nếu thiếu thư viện ncclient = pytest.importorskip("ncclient")
Biết là đang lỗi @pytest.mark.xfail(reason="bug #12")
Mark tự đặt (phải khai báo trong markers) @pytest.mark.slow
So sánh số thực assert 0.1 + 0.2 == pytest.approx(0.3)
Kiểm tra exception và message with pytest.raises(ValueError, match="regex"):
Kiểm tra warning with pytest.warns(DeprecationWarning):

Fixture có sẵn hay dùng

Fixture Dùng để
tmp_path Thư mục tạm riêng mỗi test (pathlib.Path): (tmp_path / "cfg.json").write_text(...)
monkeypatch monkeypatch.setenv("NET_USER", "bot"), delenv(...), setattr("mod.func", fake), chdir(tmp_path); tự hoàn tác sau test
capsys Bắt stdout/stderr: capsys.readouterr().out
caplog Bắt log: assert "timeout" in caplog.text, đổi level caplog.set_level(logging.DEBUG)
request Thông tin test hiện tại, request.param trong fixture có params=

Cấu hình và coverage

pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers"          # mark gõ sai tên -> lỗi thay vì bị bỏ qua
markers = [
  "slow: test chạy lâu",
  "lab: cần thiết bị lab",
]
Mục đích Lệnh
Coverage cho package app, hiện dòng chưa test pytest --cov=app --cov-report=term-missing
Xuất HTML / XML cho CI --cov-report=html / --cov-report=xml
Fail nếu coverage dưới ngưỡng pytest --cov=app --cov-fail-under=80
Đo cả nhánh if/else pytest --cov=app --cov-branch

Test chạm thiết bị thật thì gắn @pytest.mark.lab và chạy CI bằng -m "not lab". Mock kết nối (netmiko, ncclient) bằng monkeypatch.setattr để test logic parse mà không cần SSH.