第 24 課 · 進階 · 18 分鐘

Python assert 測試

把預期行為變成可以反覆執行的檢查。

assert測試AssertionError邊界情況

Maya 改了總價公式,得確認那張 18.75 的訂單還沒被改壞。一條 assert 一秒就能回答。什麼時候 assert 是錯的工具?

assert 語句會檢查一個條件,條件為假時拋出 AssertionError。學習階段,它是記錄並驗證預期的緊湊方式。它適合內部檢查和測試,不適合驗證不可信的使用者輸入。 你還會用表格驅動用例、斷言預期例外,並用容差比較浮點數。

斷言一個事實

assert add(2, 3) == 5 用可執行的形式寫出預期結果。如果函式以後改成回傳 6,程式會停在這一行。逗號後面的短訊息可以說明哪裡不符合預期。

def add(a, b):
    return a + b
assert add(2, 3) == 5
print("ok")

一個通過的檢查

測試多個例子

少量斷言就能覆蓋正常輸入和一個邊界情況。最重要的行為放在前面。某個斷言失敗時,Python 會報告行號並停止目前測試,後面的檢查不會執行。

def slugify(text):
    return text.strip().lower().replace(" ", "-")
assert slugify(" Hello World ") == "hello-world"
assert slugify("Python") == "python"
print("2 tests passed")

不止一個例子

捕獲失敗並查看原因

AssertionError 和其他例外一樣。在小範例裡捕獲它,可以只列印訊息而不顯示整段 traceback。真實測試套件中應讓測試執行器報告失敗,而不是把失敗藏起來。

def double(n):
    return n * 2
try:
    assert double(3) == 7, "double(3) should be 6"
except AssertionError as error:
    print(error)

讀取失敗訊息

把用例放進表格

用 (輸入, 預期) 列表把覆蓋範圍擺出來,每條斷言也能保持短小。訊息裡用 repr(),失敗時能看見準確的值。第一條失敗的 assert 會停止執行,所以測試執行器一次只報告一個失敗。

def slugify(text):
    return text.strip().lower().replace(" ", "-")

cases = [(" Hello World ", "hello-world"), ("Python", "python"), ("", "")]
for raw, expected in cases:
    actual = slugify(raw)
    assert actual == expected, f"{raw!r}: {actual!r} != {expected!r}"
print(len(cases), "cases passed")

一張表,一條清楚的失敗訊息

斷言例外,而不是保持沉默

有些行為本來就應該報錯。呼叫函式,只捕獲預期例外,並斷言它確實發生;else 分支負責抓「沒有報錯」的情況。真實測試套件裡用 pytest.raises 或 unittest 的 assertRaises,不要用裸 try 把失敗藏起來。

def parse_age(raw):
    value = int(raw)
    if value < 0:
        raise ValueError("age must be non-negative")
    return value

for raw in ["-1", "many"]:
    try:
        parse_age(raw)
    except ValueError as error:
        print(raw, type(error).__name__, error)
    else:
        print("no error", raw)

預期失敗也是契約的一部分

浮點數用容差比較

二進位浮點會讓 0.1 + 0.2 與 0.3 略有差異。要求精確相等的測試測的是表示方式,不是目標值。用 math.isclose 並寫明容差;金額場景則比較整數分。

import math
total = 0.1 + 0.2
print(total == 0.3)
print(math.isclose(total, 0.3, rel_tol=1e-9))
assert math.isclose(total, 0.3, rel_tol=1e-9)
print("close enough")

在規定容差內測試值

本課要點

  • assert condition 在條件為假時拋出 AssertionError。
  • 逗號後面的字串可以補充說明預期。
  • 斷言用於測試和內部不變數,不用於使用者輸入驗證。
  • Python 的 -O 參數會移除 assert,所以不能用它做安全驗證。
  • 測試要有可見用例、明確的預期例外,以及對浮點數的容差。

常見錯誤

  • 用 assert 驗證表單或密碼。
  • 只測試順利路徑,跳過空值和邊界輸入。
  • 錯誤訊息太模糊,沒有說明預期是什麼。
  • 用 assert 驗證使用者輸入;-O 會把斷言從執行中的程式移除。
輪到你

寫一個 square(n),斷言 square(4) == 16 和 square(0) == 0,然後列印 'ok'。

動手試試

改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。

瀏覽器內 Python本機執行 · 無需帳號
首次執行會從 CDN 下載 Pyodide。修改後點擊「執行程式碼」。

練習本課

12 道題,作答後立即看到解釋。

查看 12 道題答案開始練習
← 日期與時間型別提示 →