unittest
是 Python 自帶的單元測試框架,用于編寫和運行可重復的測試用例。它的核心思想是通過斷言(assertions)驗證代碼的行為是否符合預期。以下是 unittest
的基本使用方法:
1. 基本結構
1.1 創建測試類
- 繼承
unittest.TestCase
,每個測試用例對應一個方法。 - 測試方法必須 以
test_
開頭,否則不會被自動識別為測試用例。
import unittestclass TestMathOperations(unittest.TestCase):def test_addition(self):self.assertEqual(1 + 1, 2) # 斷言 1+1=2
1.2 前置與后置方法
setUp()
: 在每個測試方法執行前運行(如初始化資源)。tearDown()
: 在每個測試方法執行后運行(如清理資源)。setUpClass()
/tearDownClass()
: 在整個測試類的開始/結束時運行(需用@classmethod
修飾)。
class TestExample(unittest.TestCase):@classmethoddef setUpClass(cls):print("整個測試類開始前執行")def setUp(self):print("每個測試方法開始前執行")def test_example(self):self.assertTrue(True)def tearDown(self):print("每個測試方法結束后執行")@classmethoddef tearDownClass(cls):print("整個測試類結束后執行")
2. 斷言方法
unittest
提供了豐富的斷言方法,常用如下:
方法 | 說明 |
---|---|
assertEqual(a, b) | 檢查 a == b |
assertTrue(x) | 檢查 x 為 True |
assertFalse(x) | 檢查 x 為 False |
assertRaises(Error, func, *args) | 檢查函數 func 是否拋出 Error 異常 |
assertIn(a, b) | 檢查 a 在 b 中 |
assertIsNone(x) | 檢查 x 是 None |
def test_assertions(self):self.assertEqual(3 * 3, 9)self.assertIn(2, [1, 2, 3])with self.assertRaises(ZeroDivisionError):_ = 1 / 0
3. 運行測試
3.1 通過代碼運行
在腳本末尾添加:
if __name__ == "__main__":unittest.main()
3.2 通過命令行運行
# 運行單個測試模塊
python -m unittest test_module.py# 自動發現并運行所有測試(推薦)
python -m unittest discover
3.3 指定運行特定測試
# 運行單個測試類
python -m unittest test_module.TestClass# 運行單個測試方法
python -m unittest test_module.TestClass.test_method
4. 測試套件(Test Suite)
手動組織多個測試用例:
suite = unittest.TestSuite()
suite.addTest(TestMathOperations("test_addition"))
suite.addTest(TestExample("test_example"))runner = unittest.TextTestRunner()
runner.run(suite)
5. 高級用法
5.1 跳過測試
使用裝飾器跳過某些測試:
@unittest.skip("跳過原因")
def test_skipped(self):self.fail("不會執行")@unittest.skipIf(condition, "條件滿足時跳過")
def test_conditional_skip(self):pass
5.2 參數化測試
unittest
本身不支持參數化,但可通過第三方庫(如 parameterized
)實現:
from parameterized import parameterizedclass TestParameterized(unittest.TestCase):@parameterized.expand([(2, 3, 5),(0, 0, 0),])def test_add(self, a, b, expected):self.assertEqual(a + b, expected)
5.3 Mock 對象
使用 unittest.mock
模擬外部依賴:
from unittest.mock import Mockdef test_mock(self):mock_obj = Mock(return_value=42)self.assertEqual(mock_obj(), 42)
6. 示例項目結構
project/
├── my_code.py # 被測試的代碼
└── tests/├── __init__.py└── test_code.py # 測試代碼
總結
unittest
是 Python 測試的基石,適合中小型項目。對于復雜場景,可以結合第三方庫(如 pytest
)增強功能。核心步驟:
- 繼承
TestCase
編寫測試類。 - 使用
test_
前綴定義測試方法。 - 通過斷言驗證邏輯。
- 利用
setUp()
/tearDown()
管理資源。 - 運行測試并分析結果。
pytest
是 Python 中最流行的第三方測試框架,以其簡潔的語法、強大的功能和靈活的擴展性著稱。相比 unittest
,pytest
更注重代碼的可讀性和可維護性,同時支持豐富的插件生態系統。以下是 pytest
的核心使用方法:
1. 安裝 pytest
pip install pytest
2. 基本用法
2.1 編寫測試函數
- 測試函數名需以
test_
開頭(或_test
結尾)。 - 斷言直接使用 Python 原生
assert
語句,無需記憶特定斷言方法。
# test_sample.py
def test_addition():assert 1 + 1 == 2def test_list_contains():numbers = [1, 2, 3]assert 2 in numbers
2.2 運行測試
# 運行當前目錄所有測試
pytest# 運行指定文件
pytest test_sample.py# 運行指定函數
pytest test_sample.py::test_addition# 顯示詳細輸出(-v 顯示用例名稱,-s 打印輸出)
pytest -v -s
3. 斷言增強
pytest
的斷言失敗信息更直觀,能自動展示上下文差異(如列表、字典比較):
def test_failure_example():expected = {"a": 1, "b": 2}actual = {"a": 1, "b": 3}assert expected == actual
運行后輸出:
AssertionError: assert {'a': 1, 'b': 2} == {'a': 1, 'b': 3}Differing items:{'b': 2} != {'b': 3}
4. Fixture(依賴注入)
pytest
的 fixture
機制用于管理測試的依賴資源(如數據庫連接、臨時文件),支持復用和共享。
4.1 定義 Fixture
import pytest@pytest.fixture
def database_connection():conn = create_db_connection() # 初始化資源yield conn # 返回資源conn.close() # 清理資源
4.2 使用 Fixture
在測試函數中通過參數名直接調用:
def test_query(database_connection):result = database_connection.query("SELECT * FROM users")assert len(result) > 0
4.3 Fixture 作用域
通過 scope
參數控制生命周期:
@pytest.fixture(scope="module") # 作用域:模塊級(每個模塊執行一次)
def shared_resource():return initialize_resource()
5. 參數化測試
使用 @pytest.mark.parametrize
對單條測試用例注入多組參數,避免重復代碼。
import pytest@pytest.mark.parametrize("a, b, expected", [(2, 3, 5),(0, 0, 0),(-1, 5, 4),
])
def test_add(a, b, expected):assert a + b == expected
6. 測試異常
使用 pytest.raises
捕獲并驗證異常:
def test_division_by_zero():with pytest.raises(ZeroDivisionError):_ = 1 / 0
7. Mock 對象(依賴隔離)
使用 pytest-mock
插件(基于 unittest.mock
)模擬外部依賴:
pip install pytest-mock
示例:
def test_mocking(mocker):mock_requests = mocker.patch("requests.get") # 模擬 requests.getmock_requests.return_value.status_code = 200response = requests.get("https://api.example.com")assert response.status_code == 200
8. 插件擴展
pytest
支持豐富的插件,例如:
pytest-cov
: 測試覆蓋率統計pytest-xdist
: 并行運行測試pytest-django
: Django 項目集成pytest-asyncio
: 異步測試支持
安裝插件:
pip install pytest-cov pytest-xdist
9. 項目結構
project/
├── src/ # 源代碼
│ └── my_module.py
└── tests/ # 測試代碼├── __init__.py├── conftest.py # 全局 Fixture 定義├── test_core.py└── test_api.py
10. 與 unittest 兼容
pytest
可以直接運行 unittest
風格的測試用例:
# test_unittest_style.py
import unittestclass TestOldCode(unittest.TestCase):def test_legacy(self):self.assertEqual(1 + 1, 2)
運行:
pytest test_unittest_style.py
11. 高級功能
-
標記(Markers):
用@pytest.mark
對測試分類(如跳過、標記為慢測試):@pytest.mark.skip(reason="尚未實現") def test_unimplemented():assert False@pytest.mark.slow def test_long_running():# 耗時操作pass
運行指定標記的測試:
pytest -m slow # 只運行標記為 slow 的測試 pytest -m "not slow" # 排除 slow 測試
-
Hook 函數:
自定義pytest
行為(如修改報告輸出)。
總結
pytest
的優勢:
- 簡潔性:使用原生
assert
,減少樣板代碼。 - 靈活性:Fixture 機制優雅管理測試依賴。
- 擴展性:通過插件支持復雜場景(如異步、分布式測試)。
- 兼容性:無縫運行
unittest
和nose
測試。
適合從簡單腳本到大型項目的全場景測試需求。