測驗:撰寫你的第一個測試:基本語法與斷言
共 5 題,點選答案後會立即顯示結果
1. pytest 如何自動找到測試函式?
2. 下列哪個檔案命名方式可以被 pytest 自動發現?
3. 在 pytest 中,如何測試一個函式是否會拋出 ValueError 例外?
4. 執行 pytest 時,測試輸出中的 FAILED 狀態代表什麼意思?
5. 下列 pytest 執行指令中,哪一個可以只執行特定的測試函式 test_add?
前言
上一篇我們了解了為什麼需要測試。現在,讓我們動手寫第一個測試。這篇文章會帶你認識 pytest 的基本語法,讓你能夠讀懂並撰寫簡單的測試程式碼。
pytest 如何找到你的測試?
當你執行 pytest 時,它會自動搜尋並執行測試。但它怎麼知道哪些是測試檔案、哪些是測試函式呢?
測試發現規則
pytest 遵循一套簡單的命名規則:
檔案層級:
- 檔名以
test開頭,例如testcalculator.py - 或者檔名以
test.py結尾,例如calculatortest.py
函式層級:
- 函式名稱必須以
test開頭,例如testadd()
讓我們看一個最小範例:
# test_hello.py
def test_greeting():
assert "hello" == "hello"
Code language: PHP (php)這就是一個完整的測試了。檔名是 test_hello.py,函式名是 test_greeting(),兩者都符合命名規則,pytest 就能找到並執行它。
為什麼這樣設計?
這種命名慣例讓你一眼就能區分「正式程式碼」和「測試程式碼」。當你在專案中看到 test_ 開頭的檔案或函式,立刻就知道:「這是測試,不是實際功能。」
assert:測試的核心
pytest 使用 Python 內建的 assert 語句進行斷言。斷言就是「我宣稱這件事是真的」,如果不是真的,測試就會失敗。
基本相等斷言
def test_basic_equality():
result = 1 + 1
assert result == 2
這段測試很直白:計算 1 + 1,斷言結果等於 2。
常見的斷言模式
def test_various_assertions():
# 相等
assert 1 + 1 == 2
# 不相等
assert 1 + 1 != 3
# 大於、小於
assert 5 > 3
assert 3 < 5
# 真假值
assert True
assert not False
# 包含關係
assert "hello" in "hello world"
assert 3 in [1, 2, 3]
# None 檢查
assert None is None
result = "something"
assert result is not None
Code language: PHP (php)讀懂失敗訊息
當斷言失敗時,pytest 會顯示詳細的錯誤訊息。這是 pytest 最強大的功能之一:
def test_will_fail():
expected = [1, 2, 3]
actual = [1, 2, 4]
assert expected == actual
執行後,你會看到類似這樣的輸出:
FAILED test_example.py::test_will_fail - AssertionError: assert [1, 2, 3] == [1, 2, 4]
Code language: PHP (php)pytest 會清楚告訴你:
- 哪個檔案的哪個測試失敗了
- 預期值和實際值分別是什麼
- 兩者的差異在哪裡
測試函式的回傳值
實際專案中,我們通常會測試自己寫的函式。以下是一個計算機的範例:
# calculator.py
def add(a, b):
return a + b
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
Code language: PHP (php)對應的測試:
# test_calculator.py
from calculator import add, divide
def test_add_positive_numbers():
result = add(2, 3)
assert result == 5
def test_add_negative_numbers():
result = add(-1, -2)
assert result == -3
def test_divide():
result = divide(10, 2)
assert result == 5.0
Code language: PHP (php)測試命名技巧
注意測試函式的命名:test_add_positive_numbers、test_add_negative_numbers。好的測試名稱會描述「測試什麼情境」,讓你一看就知道這個測試的目的。
測試例外:pytest.raises
有時候,我們預期函式會拋出例外。例如,除以零應該要報錯。pytest 提供了 pytest.raises 來測試這種情況:
import pytest
from calculator import divide
def test_divide_by_zero():
with pytest.raises(ValueError):
divide(10, 0)
Code language: JavaScript (javascript)這段程式碼的意思是:「我預期 divide(10, 0) 會拋出 ValueError。」如果沒有拋出例外,或拋出了其他類型的例外,測試就會失敗。
檢查例外訊息
你還可以進一步檢查例外的訊息內容:
def test_divide_by_zero_message():
with pytest.raises(ValueError) as exc_info:
divide(10, 0)
assert "Cannot divide by zero" in str(exc_info.value)
Code language: JavaScript (javascript)exc_info.value 包含了被捕捉到的例外物件,你可以檢查它的訊息是否符合預期。
執行測試
基本執行方式
# 執行當前目錄下所有測試
pytest
# 執行特定檔案
pytest test_calculator.py
# 執行特定測試函式
pytest test_calculator.py::test_add_positive_numbers
Code language: PHP (php)常用選項
# -v:顯示詳細輸出,列出每個測試的名稱和結果
pytest -v
# -q:安靜模式,只顯示摘要
pytest -q
# --tb=short:簡化錯誤訊息
pytest --tb=short
Code language: PHP (php)解讀測試輸出
執行 pytest -v 後,你會看到類似這樣的輸出:
==================== test session starts ====================
collected 4 items
test_calculator.py::test_add_positive_numbers PASSED
test_calculator.py::test_add_negative_numbers PASSED
test_calculator.py::test_divide PASSED
test_calculator.py::test_divide_by_zero PASSED
==================== 4 passed in 0.02s ====================
Code language: PHP (php)狀態標記說明
- PASSED:測試通過,一切正常
- FAILED:斷言失敗,結果不符合預期
- ERROR:測試程式碼本身有錯誤(例如 import 失敗)
- SKIPPED:測試被跳過(通常是故意的)
失敗時的輸出
當測試失敗時,pytest 會顯示詳細的除錯資訊:
==================== FAILURES ====================
__________________ test_will_fail __________________
def test_will_fail():
expected = [1, 2, 3]
actual = [1, 2, 4]
> assert expected == actual
E AssertionError: assert [1, 2, 3] == [1, 2, 4]
E At index 2 diff: 3 != 4
test_example.py:4: AssertionError
這個輸出告訴你:
- 哪個測試失敗了(
testwillfail) - 失敗的那行程式碼(
>標記) - 錯誤類型和詳細比較(
E開頭的行) - 失敗位置(
test_example.py:4)
組織測試檔案
隨著專案成長,你需要妥善組織測試檔案。一個常見的結構是:
my_project/
├── src/
│ ├── calculator.py
│ └── utils.py
├── tests/
│ ├── test_calculator.py
│ └── test_utils.py
└── pytest.ini
命名對應原則
一個好習慣是讓測試檔案與被測試的模組一一對應:
calculator.py對應test_calculator.pyutils.py對應test_utils.py
這樣當你需要找某個模組的測試時,馬上就知道該看哪個檔案。
重點回顧
- 命名規則:檔案用
test.py或test.py,函式用test_* - assert 斷言:直接使用 Python 的
assert語句 - 測試例外:使用
pytest.raises(ExceptionType)包裝 - 執行測試:
pytest或pytest -v看詳細結果 - 解讀輸出:PASSED 表示通過,FAILED 表示失敗
下一步
現在你已經能寫基本的測試了。下一篇,我們會介紹 fixture,這是 pytest 最重要的功能之一,讓你能夠重複使用測試的準備工作,大幅減少重複程式碼。
進階測驗:撰寫你的第一個測試:基本語法與斷言
共 5 題,包含情境題與錯誤診斷題。