跳轉至

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

assert 與例外測試

pytest 用 Python 內建的 assert 敘述判斷測試是否通過,並在失敗時自動顯示比較雙方的實際值;預期程式「應該拋出例外」時則用 pytest.raises。本頁整理比對一般值、浮點數、例外與警告的寫法。

用 assert 比對結果

任何結果為真(truthy)的運算式都可以放在 assert 後面:

def test_assert_examples():
    assert 1 + 1 == 2
    assert "py" in "pytest"
    assert [1, 2, 3] == [1, 2, 3]
    assert {"a": 1}.get("b") is None
    assert len([]) == 0

比對串列、字典、長字串失敗時,pytest 會列出差異之處,不用自己逐項比對:

E       AssertionError: assert {'name': 'Amy', 'age': 30} == {'name': 'Amy', 'age': 31}
E         Differing items:
E         {'age': 30} != {'age': 31}

需要補充說明時,可以在逗號後加上訊息,失敗時會一併顯示:

assert response.status_code == 200, f"API 回應異常:{response.text}"

一個測試驗證一件事

一個測試可以有多個 assert,但最好都在驗證同一個行為。第一個 assert 失敗後,後面的不會執行;若想分別知道每個條件的結果,就拆成多個測試或改用 參數化。

浮點數比對:pytest.approx

浮點數有精度誤差,0.1 + 0.2 == 0.3 在 Python 中是 False。用 pytest.approx 比對「足夠接近」:

import pytest


def test_float():
    assert 0.1 + 0.2 == pytest.approx(0.3)
    assert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
    assert 10.05 == pytest.approx(10, abs=0.1)   # 指定絕對誤差

預設容許的相對誤差是 1e-6,也能用 rel= 或 abs= 調整。

測試例外:pytest.raises

「輸入錯誤時要拋出 ValueError」本身就是一個需要測試的行為。把預期會出錯的程式碼放進 with pytest.raises(...) 區塊:

import pytest
from shop import apply_discount


def test_invalid_percent_raises():
    with pytest.raises(ValueError):
        apply_discount(100, 150)

區塊內若沒有拋出 ValueError,測試就失敗。

如何同時驗證例外訊息?

用 match= 參數比對錯誤訊息。match 是正規表示式(regular expression),訊息中有 .、( 等特殊字元時要用 re.escape() 轉義:

def test_invalid_percent_message():
    with pytest.raises(ValueError, match="0 到 100"):
        apply_discount(100, -5)

需要檢查例外物件的其他屬性時,用 as 取得 ExceptionInfo:

def test_exception_info():
    with pytest.raises(ValueError) as excinfo:
        apply_discount(100, 150)
    assert "percent" in str(excinfo.value)

區塊只放會出錯的那一行

with pytest.raises 區塊裡如果放了好幾行,可能是「錯的那一行」先拋出相同類型的例外,測試仍然通過,卻沒有測到想測的東西。

測試警告:pytest.warns

程式用 warnings.warn() 發出棄用(deprecation)等警告時,用 pytest.warns 驗證:

import warnings

import pytest


def old_api():
    warnings.warn("old_api 已棄用,請改用 new_api", DeprecationWarning)
    return 1


def test_old_api_warns():
    with pytest.warns(DeprecationWarning, match="已棄用"):
        assert old_api() == 1

推薦影音

pytest: testing exceptions

pytest 核心開發者 Anthony Sottile 約 12 分鐘講解如何用 pytest 測試「程式應該拋出例外」的情況,對應本頁「測試例外」一節。