python-testing-patterns 是做什麼的?
使用 pytest、fixtures、mocking 和測試驅動開發來實現全面的測試策略。在編寫 Python 測試、設置測試套件或實施測試最佳實踐時使用。
概述
此技能提供了一套完整的 Python 測試模式與最佳實踐,主要圍繞 pytest 生態系統。它涵蓋了從基本測試撰寫到進階主題(如 fixtures、mocking、參數化測試和 TDD)的所有內容。無論是為現有專案新增測試,還是從零開始建立測試套件,此模式都能協助你創建可靠且易於維護的測試。
核心模式
1. 基礎測試結構
import pytest
from my_module import add
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
2. 使用 Fixtures
Fixtures 允許你建立可重複使用的測試元件。
import pytest
@pytest.fixture
def database():
# 設置:建立資料庫連接
db = create_database()
yield db
# 清理:關閉連接
db.close()
def test_query(database):
result = database.query("SELECT * FROM users")
assert len(result) > 0
3. Mocking 外部依賴
使用 unittest.mock 來隔離測試與外部服務。
from unittest.mock import Mock, patch
def test_api_call():
with patch('my_module.requests.get') as mock_get:
mock_get.return_value.json.return_value = {'key': 'value'}
response = my_function()
assert response == {'key': 'value'}
4. 測試驅動開發 (TDD) 模式
遵循紅-綠-重構循環:
- 紅:先編寫一個會失敗的測試。
- 綠:撰寫最簡單的程式碼讓測試通過。
- 重構:在不破壞測試的情況下改進程式碼。
5. 參數化測試
使用參數化減少重複程式碼。
@pytest.mark.parametrize("input,expected", [
(1, 1),
(2, 4),
(3, 9),
])
def test_square(input, expected):
assert square(input) == expected
最佳實踐
- 使用明確的測試名稱以描述預期行為。
- 保持測試獨立且可隔離執行。
- 善用 Fixtures 進行設置和清理。
- 將 Mock 僅用於測試邊界。
- 採用 Given-When-Then 模式撰寫測試。
- 將測試檔案放在
tests/目錄中。
進階 Fixtures
範圍 Fixtures
@pytest.fixture(scope="session")
def global_config():
return load_config()
支援的範圍:function(預設)、class、module、session。
使用 Fixtures 的 Fixtures
@pytest.fixture
def user(database):
return database.create_user(username="test")
非同步測試
支援非同步測試。
@pytest.mark.asyncio
async def test_async_function():
result = await async_function()
assert result == expected_value
錯誤測試
def test_raises_exception():
with pytest.raises(ValueError):
function_that_raises()
覆蓋率
確保高覆蓋率。
pytest --cov=my_module tests/
持續整合
整合至 CI 流程。
# .github/workflows/test.yml
name: Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run tests
run: pytest --cov-report=xml
疑難排解
- 確保 pytest 已安裝:
pip install pytest pytest-cov pytest-asyncio - 檢查測試發現規則。
- 使用
pytest -v獲取詳細輸出。 - 使用
pytest --tb=short縮短回溯資訊。
整合範例
結合多個模式。
import pytest
from unittest.mock import patch
@pytest.fixture
def mock_external_service():
with patch('my_module.external_service') as mock:
mock.return_value = "mocked"
yield mock
def test_integration(mock_external_service):
result = my_function()
assert result == "mocked"
常見陷阱
- 測試順序依賴:避免測試間互相依賴。
- 過度 Mock:只 mock 你需要隔離的元件。
- 忽略邊界情況:測試邊界值。
- 不充分的清理:確保 Fixtures 能正確清理。