pytest 允許您使用標準 Python 斷言來驗證 Python 測試中的期望和值。 例如,您可以編寫以下內容:
# content of test_assert1.py
def f():
return 3
def test_function():
assert f() == 4
斷言您的函數(shù)返回某個值。 如果此斷言失敗,您將看到函數(shù)調用的返回值:
$ pytest test_assert1.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item
test_assert1.py F [100%]
================================= FAILURES =================================
______________________________ test_function _______________________________
def test_function():
> assert f() == 4
E assert 3 == 4
E + where 3 = f()
test_assert1.py:6: AssertionError
========================= short test summary info ==========================
FAILED test_assert1.py::test_function - assert 3 == 4
============================ 1 failed in 0.12s =============================
Pytest支持顯示最常用的子表達式的值,包括調用、屬性、比較以及二進制和一元操作符。這允許您在不使用樣板代碼的情況下使用慣用的python構造,同時不丟失內省信息。
但是,如果您使用這樣的斷言指定消息:
assert a % 2 == 0, "value was odd, should be even"
然后,根本不會發(fā)生任何斷言內省,消息將簡單地顯示在回溯中。
為了編寫有關引發(fā)異常的斷言,您可以使用 ?pytest.raises()
? 作為上下文管理器,如下所示:
import pytest
def test_zero_division():
with pytest.raises(ZeroDivisionError):
1 / 0
如果你需要訪問實際的異常信息,你可以使用:
def test_recursion_depth():
with pytest.raises(RuntimeError) as excinfo:
def f():
f()
f()
assert "maximum recursion" in str(excinfo.value)
?excinfo
?是一個?ExceptionInfo
?實例,它包裝了實際引發(fā)的異常。interest的主要屬性是?.type
?、?.value
?和?.traceback
?
您可以向上下文管理器傳遞一個?match
?關鍵字參數(shù),以測試正則表達式是否匹配異常的字符串表示(類似于 ?unittest
?中的 ?TestCase.assertRaisesRegex
? 方法):
import pytest
def myfunc():
raise ValueError("Exception 123 raised")
def test_match():
with pytest.raises(ValueError, match=r".* 123 .*"):
myfunc()
?match
?方法的 ?regexp
?參數(shù)與 ?re.search
? 函數(shù)匹配,因此在上面的示例中 ?match='123'
? 也可以正常工作。
?pytest.raises()
? 函數(shù)還有另一種形式,您可以在其中傳遞一個函數(shù),該函數(shù)將使用給定的 ?*args
? 和 ?**kwargs
? 執(zhí)行,并斷言引發(fā)了給定的異常:
pytest.raises(ExpectedException, func, *args, **kwargs)
如果出現(xiàn)無異常或錯誤異常等故障,?reporter
?將為您提供有用的輸出。
請注意,也可以為 ?pytest.mark.xfail
? 指定一個?raises
?參數(shù),它以更具體的方式檢查測試是否失敗,而不僅僅是引發(fā)任何異常:
@pytest.mark.xfail(raises=IndexError)
def test_f():
f()
使用 ?pytest.raises()
? 對于您正在測試自己的代碼故意引發(fā)的異常的情況可能會更好,而使用帶有檢查功能的?@pytest.mark.xfail
? 可能更適合記錄未修復的錯誤(其中測試描述了應該發(fā)生什么)或依賴項中的錯誤。
您可以使用 ?pytest.warns
? 檢查代碼是否引發(fā)了特定警告。
Pytest具有豐富的支持,可以在遇到比較時提供上下文敏感的信息。例如:
# content of test_assert2.py
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
assert set1 == set2
如果你運行這個模塊:
$ pytest test_assert2.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-7.x.y, pluggy-1.x.y
rootdir: /home/sweet/project
collected 1 item
test_assert2.py F [100%]
================================= FAILURES =================================
___________________________ test_set_comparison ____________________________
def test_set_comparison():
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E AssertionError: assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
E '5'
E Use -v to get more diff
test_assert2.py:4: AssertionError
========================= short test summary info ==========================
FAILED test_assert2.py::test_set_comparison - AssertionError: assert {'0'...
============================ 1 failed in 0.12s =============================
對一些情況進行了特殊比較:
可以通過實現(xiàn)?pytest_assertrepr_compare
?鉤子來添加您自己的詳細解釋。
返回失敗的斷言表達式中比較的解釋。
如果沒有自定義解釋,則返回?None
?,否則返回一個字符串列表。字符串將由換行符連接,但字符串中的任何換行符將被轉義。請注意,除第一行外的所有內容都稍微縮進,目的是將第一行作為摘要。
參數(shù):
config (pytest.Config)
? -- pytest 配置對象op (str)
? –
left (object)
? –
right (object)
? –返回類型:
Optional[List[str]]
例如,可以考慮在?conftest.py
?文件中添加以下鉤子,它提供了對?Foo
?對象的另一種解釋:
# content of conftest.py
from test_foocompare import Foo
def pytest_assertrepr_compare(op, left, right):
if isinstance(left, Foo) and isinstance(right, Foo) and op == "==":
return [
"Comparing Foo instances:",
" vals: {} != {}".format(left.val, right.val),
]
現(xiàn)在,給定這個測試模塊:
# content of test_foocompare.py
class Foo:
def __init__(self, val):
self.val = val
def __eq__(self, other):
return self.val == other.val
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
assert f1 == f2
你可以運行?test
?模塊,并獲得在?conftest
?文件中定義的自定義輸出:
$ pytest -q test_foocompare.py
F [100%]
================================= FAILURES =================================
_______________________________ test_compare _______________________________
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
> assert f1 == f2
E assert Comparing Foo instances:
E vals: 1 != 2
test_foocompare.py:12: AssertionError
========================= short test summary info ==========================
FAILED test_foocompare.py::test_compare - assert Comparing Foo instances:
1 failed in 0.12s
通過在?assert
?語句運行之前重寫它們,可以報告關于失敗斷言的詳細信息。重寫的斷言語句將自省信息放入斷言失敗消息中。pytest只重寫由其測試收集過程直接發(fā)現(xiàn)的測試模塊,因此在不屬于測試模塊的支持模塊中的斷言不會被重寫。
您可以在導入模塊之前通過調用 ?register_assert_rewrite
?手動為導入的模塊啟用斷言重寫(這樣做的好地方是在您的根目錄 ?conftest.py
? 中)。
pytest 會將重寫的模塊寫回磁盤進行緩存。 您可以通過將其添加到 ?conftest.py
? 文件的頂部來禁用此行為(例如,避免在經(jīng)常移動文件的項目中留下陳舊的? .pyc
? 文件):
import sys
sys.dont_write_bytecode = True
請注意,您仍然可以獲得斷言自省的好處,唯一的變化是 ?.pyc
? 文件不會緩存在磁盤上。
此外,如果無法寫入新的 ?.pyc
? 文件,即在只讀文件系統(tǒng)或 zip 文件中,重寫將靜默跳過緩存。
pytest 在導入時重寫測試模塊,方法是使用導入鉤子編寫新的 ?pyc
? 文件。大多數(shù)情況下,這是透明的。如果您自己使用導入,導入鉤子可能會干擾。
如果是這種情況,你有兩個選擇:
PYTEST_DONT_REWRITE
?添加到其文檔字符串中,禁用特定模塊的重寫。assert=plain
?禁用所有模塊的重寫。
更多建議: