開始使用¶
安裝 pytest
¶
pytest
需要:Python 3.8+ 或 PyPy3。
在命令列中執行以下命令
pip install -U pytest
檢查您是否安裝了正確的版本
$ pytest --version
pytest 8.3.5
建立您的第一個測試¶
建立一個名為 test_sample.py
的新檔案,其中包含一個函數和一個測試
# content of test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
測試
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-8.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item
test_sample.py F [100%]
================================= FAILURES =================================
_______________________________ test_answer ________________________________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:6: AssertionError
========================= short test summary info ==========================
FAILED test_sample.py::test_answer - assert 4 == 5
============================ 1 failed in 0.12s =============================
[100%]
指的是執行所有測試案例的總進度。完成後,pytest 會顯示失敗報告,因為 func(3)
沒有返回 5
。
注意
您可以使用 assert
語句來驗證測試預期。pytest 的進階斷言內省將智慧地報告斷言表達式的中間值,因此您可以避免許多 JUnit 傳統方法 的名稱。
執行多個測試¶
pytest
將在目前目錄及其子目錄中執行所有 test_*.py 或 *_test.py 形式的檔案。更廣泛地說,它遵循標準測試發現規則。
斷言引發了某個異常¶
使用 raises 輔助函數來斷言某些程式碼會引發異常
# content of test_sysexit.py
import pytest
def f():
raise SystemExit(1)
def test_mytest():
with pytest.raises(SystemExit):
f()
您也可以使用 raises 提供的上下文來斷言預期的異常是引發的 ExceptionGroup
的一部分
# content of test_exceptiongroup.py
import pytest
def f():
raise ExceptionGroup(
"Group message",
[
RuntimeError(),
],
)
def test_exception_in_group():
with pytest.raises(ExceptionGroup) as excinfo:
f()
assert excinfo.group_contains(RuntimeError)
assert not excinfo.group_contains(TypeError)
以 “quiet” 報告模式執行測試函數
$ pytest -q test_sysexit.py
. [100%]
1 passed in 0.12s
注意
-q/--quiet
標誌使此範例和後續範例中的輸出保持簡潔。
在類別中群組多個測試¶
一旦您開發了多個測試,您可能希望將它們分組到一個類別中。pytest 使建立包含多個測試的類別變得容易
# content of test_class.py
class TestClass:
def test_one(self):
x = "this"
assert "h" in x
def test_two(self):
x = "hello"
assert hasattr(x, "check")
pytest
發現所有遵循其Python 測試發現慣例的測試,因此它找到了 test_
前綴的函數。無需子類別化任何內容,但請確保以 Test
為您的類別加上前綴,否則該類別將被跳過。我們可以通過傳遞其檔案名稱來簡單地執行模組
$ pytest -q test_class.py
.F [100%]
================================= FAILURES =================================
____________________________ TestClass.test_two ____________________________
self = <test_class.TestClass object at 0xdeadbeef0001>
def test_two(self):
x = "hello"
> assert hasattr(x, "check")
E AssertionError: assert False
E + where False = hasattr('hello', 'check')
test_class.py:8: AssertionError
========================= short test summary info ==========================
FAILED test_class.py::TestClass::test_two - AssertionError: assert False
1 failed, 1 passed in 0.12s
第一個測試通過,第二個測試失敗。您可以輕鬆地在斷言中看到中間值,以幫助您理解失敗的原因。
在類別中群組測試可能出於以下原因而有益
測試組織
僅在該特定類別中為測試共享 fixtures
在類別層級套用標記,並使其隱式地套用於所有測試
在類別內群組測試時需要注意的一點是,每個測試都有該類別的唯一實例。讓每個測試共享同一個類別實例將非常不利於測試隔離,並會助長不良的測試實踐。這在下面概述
# content of test_class_demo.py
class TestClassDemoInstance:
value = 0
def test_one(self):
self.value = 1
assert self.value == 1
def test_two(self):
assert self.value == 1
$ pytest -k TestClassDemoInstance -q
.F [100%]
================================= FAILURES =================================
______________________ TestClassDemoInstance.test_two ______________________
self = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>
def test_two(self):
> assert self.value == 1
E assert 0 == 1
E + where 0 = <test_class_demo.TestClassDemoInstance object at 0xdeadbeef0002>.value
test_class_demo.py:9: AssertionError
========================= short test summary info ==========================
FAILED test_class_demo.py::TestClassDemoInstance::test_two - assert 0 == 1
1 failed, 1 passed in 0.12s
請注意,在類別層級新增的屬性是類別屬性,因此它們將在測試之間共享。
為功能測試請求唯一的臨時目錄¶
pytest
提供內建 fixtures/函數參數來請求任意資源,例如唯一的臨時目錄
# content of test_tmp_path.py
def test_needsfiles(tmp_path):
print(tmp_path)
assert 0
在測試函數簽名中列出名稱 tmp_path
,並且 pytest
將查找並調用 fixture 工廠以在執行測試函數調用之前建立資源。在測試執行之前,pytest
會建立每個測試調用唯一的臨時目錄
$ pytest -q test_tmp_path.py
F [100%]
================================= FAILURES =================================
_____________________________ test_needsfiles ______________________________
tmp_path = PosixPath('PYTEST_TMPDIR/test_needsfiles0')
def test_needsfiles(tmp_path):
print(tmp_path)
> assert 0
E assert 0
test_tmp_path.py:3: AssertionError
--------------------------- Captured stdout call ---------------------------
PYTEST_TMPDIR/test_needsfiles0
========================= short test summary info ==========================
FAILED test_tmp_path.py::test_needsfiles - assert 0
1 failed in 0.12s
有關臨時目錄處理的更多資訊,請參閱臨時目錄和檔案。
使用以下命令找出存在哪些類型的內建 pytest fixtures
pytest --fixtures # shows builtin and custom fixtures
請注意,此命令省略了帶有前導 _
的 fixtures,除非新增了 -v
選項。
繼續閱讀¶
查看其他 pytest 資源,以幫助您為獨特的工作流程自訂測試
“如何調用 pytest” 以取得命令列調用範例
“如何在現有的測試套件中使用 pytest” 以使用預先存在的測試
“如何使用屬性標記測試函數” 以取得有關
pytest.mark
機制的資訊“Fixtures 參考” 為您的測試提供功能基準
“編寫 plugins” 用於管理和編寫 plugins
“良好的整合實踐” 用於 virtualenv 和測試佈局