Python Pytest 單元測試
測試是確保程式碼品質與長期維護性的關鍵。雖然 Python 內建了 unittest 模組,但現代 Python 開發者更傾向於使用 Pytest。Pytest 以其簡潔的語法、功能強大的 Fixtures 以及豐富的擴展生態系,成為了 Python 測試領域的事實標準。
為什麼選擇 Pytest 而非 unittest?
- 語法簡潔:不需要繼承特定類別或使用物件導向模式,直接撰寫函式即可。
- 自然的斷言:直接使用 Python 內建的
assert標誌,不需要記住assertEqual,assertTrue等專用屬性。 - 強大的 Fixture 系統:提供了一種優雅且具備依賴注入特色的方式來管理測試環境與資料。
- 豐富的插件:例如
pytest-mock(模擬對象)、pytest-cov(覆蓋率報告)、pytest-xdist(平行執行) 等。
安裝
pip install pytest
基礎約定
Pytest 會自動偵測當前目錄下的測試檔案。為了讓 Pytest 找到你的測試,請遵循以下命名約定:
- 測試檔案名稱應以
test_*.py或*_test.py結尾。 - 測試函式名稱應以
test_開頭。
簡單範例
# calculator.py
def add(a, b):
return a + b
# test_calculator.py
import pytest
from calculator import add
def test_add():
# 直接使用 assert,出錯時 Pytest 會提供詳細的變數差異比較
assert add(1, 2) == 3
assert add(-1, 1) == 0
def test_type_error():
# 測試是否會如預期般拋出異常
with pytest.raises(TypeError):
add(1, "2")
執行測試(在終端機輸入):
pytest
核心功能:Fixtures
Fixture 是 Pytest 的精髓。它讓你將「準備工作 (Setup)」與「清理工作 (Teardown)」獨立出來,並透過參數注入的方式提供給需要的測試案例。
基本 Fixture 與作用域
import pytest
@pytest.fixture(scope="function") # 每個測試函式執行前都會重新執行一次
def db_connection():
print("\n[Setup] 建立資料庫連線")
conn = {"status": "connected"}
yield conn # yield 之前是 Setup 邏輯
# yield 之後是 Teardown (清理) 邏輯
print("\n[Teardown] 關閉資料庫連線")
def test_query(db_connection):
# db_connection 參數會自動接收到 yield 出來的值
assert db_connection["status"] == "connected"
使用 conftest.py 共享設定
如果你有多個測試檔案都需要相同的 Fixture,可以將它們寫在根目錄的 conftest.py 中。Pytest 會自動載入該檔案,你不需要手動 import 即可在所有測試檔案中直接使用那些 Fixture。
參數化測試 (Parametrize)
當你需要用多組資料測試同一個演算法或邏輯時,parametrize 能極大減少重複代碼。
import pytest
@pytest.mark.parametrize("x, y, expected", [
(1, 2, 3),
(10, 20, 30),
(-1, 1, 0),
(0, 0, 0),
])
def test_addition(x, y, expected):
assert x + y == expected
標記 (Markers)
你可以標注特定的測試案例,以便於分類或控制執行行為。
@pytest.mark.skip(reason="還沒開發完"):跳過此測試。@pytest.mark.xfail:預期此測試會失敗(可用於標記已知的 Bug)。- 自定義標記:
執行時可透過@pytest.mark.slow def test_heavy_task(): ...pytest -m slow只跑特定的測試。
常用指令技巧
pytest -v:Verbose 模式,顯示每個測試案例的完整名稱與結果。pytest -s:允許在輸出中顯示print()的內容。pytest -k "user":只執行名稱包含 "user" 的測試案例。pytest --maxfail=2:在發生 2 次錯誤後立即停止執行(節省時間)。
總結
- Pytest 的開發門檻低於
unittest,但上限更高。 - 善用 assert 簡化斷言邏輯。
- 學習 Fixtures 來打造專業的測試套件結構。
- conftest.py 是組織跨檔案測試設定的關鍵。
對於任何實務的 Python 專案,建立良好的 Pytest 測試習慣不僅能減少線上 Bug,更能讓你勇敢地重構程式碼,因為測試就是你的最強後盾。