跳轉至

建立 2026-10-11 更新 2026-10-11

Marker

Marker 是貼在測試上的標籤,寫法是 @pytest.mark.名稱。內建 marker 能控制測試是否執行,例如 skip 跳過、skipif 有條件跳過、xfail 預期失敗;自訂 marker 則用來分類,例如把慢的測試標成 slow,再用 -m 選擇要不要執行。

跳過測試:skip 與 skipif

import sys

import pytest


@pytest.mark.skip(reason="金流沙盒維護中")
def test_payment():
    ...


@pytest.mark.skipif(sys.platform == "win32", reason="只在 Linux/macOS 上測試")
def test_unix_permission():
    ...

skipif 的條件在收集測試時就會判斷。reason 一定要寫,執行 pytest -ra 時會顯示在摘要裡,讓人知道為什麼跳過。

需要在測試執行中途才決定跳過時,呼叫 pytest.skip();缺少選用套件時,可以用 pytest.importorskip():

def test_with_numpy():
    np = pytest.importorskip("numpy")   # 沒安裝 numpy 就跳過
    assert np.array([1, 2]).sum() == 3

預期失敗:xfail

xfail 表示「已知這個測試目前會失敗」,例如 bug 已回報但還沒修好。失敗時顯示 x(xfailed),不會讓整體測試失敗:

@pytest.mark.xfail(reason="issue #42:負數價格尚未檢查")
def test_negative_price():
    with pytest.raises(ValueError):
        apply_discount(-100, 10)

skip 和 xfail 差在哪?

skip 根本不執行測試;xfail 會執行,只是失敗也不算錯。xfail 的測試若意外通過,會顯示 X(xpassed),提醒你 bug 可能已經修好、可以拿掉標記了。加上 strict=True 時,意外通過會被當成失敗,確保標記不會被遺忘。

自訂 marker 為測試分類

@pytest.mark.slow
def test_big_report():
    ...


@pytest.mark.integration
def test_call_real_api():
    ...

用 -m 選擇:

pytest -m slow                        # 只跑 slow
pytest -m "not slow"                  # 日常開發排除慢的測試
pytest -m "integration and not slow"

為什麼要註冊 marker?

自訂 marker 應該在設定檔中註冊。沒註冊時 pytest 只會發出警告,打錯字(例如 @pytest.mark.slwo)不容易發現;加上 --strict-markers 後,未註冊的 marker 會直接報錯:

pyproject.toml
[tool.pytest]
addopts = ["--strict-markers"]
markers = [
    "slow: 執行時間較長的測試",
    "integration: 會呼叫外部服務的整合測試",
]

註冊後執行 pytest --markers 就能看到所有 marker 與說明。

套用到整個類別或檔案

marker 加在類別上會套用到其中所有測試;要套用到整個檔案,在模組層級設定 pytestmark:

import pytest

pytestmark = pytest.mark.integration   # 本檔所有測試都是整合測試

推薦影音

pytest Markers - Custom and Built-In Markers for Test Filtering

BugBytes 約 15 分鐘的示範,依序介紹 skip、skipif、xfail、自訂 marker,以及用 -m 篩選要執行的測試,最後提到 pytest-django 的 django_db marker。