斷言的編寫和報告
使用assert語句進行斷言
pytest允許你使用標準的Pythonassert斷言語句來驗證測試中的期望結(jié)果和實際結(jié)果。 例如,你可以編寫以下內(nèi)容:
# test_assert1.py文件內(nèi)容 def f(): return 3 def test_function(): assert f() == 4
來斷言你的函數(shù)返回一個特定的值。 如果此斷言失敗,你將看到函數(shù)調(diào)用的返回值:
$ pytest test_assert1.py =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile: 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:5: AssertionError =========================
1 failed in 0.12 seconds =========================
pytest支持顯示常見的包括調(diào)用,屬性,比較以及二元和一元運算符子表達式的值 (參考:pytest執(zhí)行Python測試失敗報告示例)。 你可以在不使用繁瑣的Python慣用構(gòu)造樣板代碼的同時,不丟失斷言失敗的對比信息(內(nèi)省信息)。
當然,你也可以像下面所示,指定斷言失敗的返回消息:
assert a % 2 == 0, "值為奇數(shù),應為偶數(shù)"
這樣將不會斷言失敗對比信息(內(nèi)省信息),而只簡單地在追溯信息中顯示你指定的失敗返回信息。
有關斷言內(nèi)省的更多信息,請參閱高級斷言內(nèi)省。
異常斷言
你可以像如下所示,使用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實例,它是實際異常的裝飾器。 其主要屬性有.type,.value及.traceback三種
版本3.0已修改
在上下文管理器中,你可以使用參數(shù)message來指定自定義失敗信息:
>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"): ... pass ... Failed: Expecting ZeroDivisionError
如果你想編寫適用于Python 2.4的測試代碼,你還可以使用其他兩種方法來測試預期的異常:
pytest.raises(ExpectedException, func, *args, **kwargs) pytest.raises(ExpectedException, "func(*args, **kwargs)")
兩者都可以對帶任意參數(shù)的函數(shù),斷言是否出現(xiàn)了期望的異常:ExpectedException。 即使沒有異常或出現(xiàn)了不同的異常,報告生成器也能輸出一些有用的斷言信息。
注意,也可以為pytest.mark.xfail指定一個“raises”參數(shù),當引發(fā)異常時標記用例失?。?br>
@pytest.mark.xfail(raises=IndexError)
def test_f():
f()
對于你在代碼中故意設置的異常,使用pytest.raises斷言更加好用,而將@ pytest.mark.xfail與check函數(shù)一起使用對于已知未修復或依賴中的bug會更好。
此外,上下文管理器表單接受match關鍵字參數(shù)來測試正則表達式匹配中的異常(如unittest中的TestCase.assertRaisesRegexp方法):
import pytest def myfunc():
raise ValueError("Exception 123 raised")
def test_match():
with pytest.raises(ValueError, match=r'.* 123 .*'): myfunc()
match變量后的正則表達式與使用re.search函數(shù)來進行匹配一致。 因此在上面的例子中,match ='123'不會引發(fā)異常。
警示斷言
2.8版本新增
你可以使用pytest.warns檢查代碼是否引發(fā)了特定警告。
使用上下文對比
2.0版本新增
pytest可以在斷言的比較中提供豐富的上下文信息。 例如:
# test_assert2.py文件內(nèi)容 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-3.x.y, py-1.x.y, pluggy-0.x.y rootdir: $REGENDOC_TMPDIR, inifile:
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 the full diff test_assert2.py:5: AssertionError =========================
1 failed in 0.12 seconds =========================
對大量用例進行了特定對比:
長字符串斷言:顯示上下文差異
長序列斷言:顯示第一個失敗的索引
字典斷言:顯示不同的key-value對
有關更多示例,請參閱報告樣例。
自定義斷言對比信息
可以通過實現(xiàn)hook方法pytest_assertrepr_compare來在斷言結(jié)果中添加你自己的詳細說明信息。
pytest_assertrepr_compare(config, op, left, right)[源碼]
返回失敗斷言表達式中的對比信息。
如果沒有自定義對比信息,則返回None,否則返回一列字符串。 字符串將由換行符連接,但字符串中的任何換行符都將被轉(zhuǎn)義。 請注意,除第一行外的所有行都將略微縮進,目的是將第一行作為摘要。
參數(shù): config(pytest.config.Config* - pytest config 對象
例如,在conftest.py文件中添加以下鉤子方法,可以為Foo對象提供了附加對比信息:
# conftest.py內(nèi)容 from test_foocompare import Foo def pytest_assertrepr_compare(op, left, right): if isinstance(left, Foo) and isinstance(right, Foo) and op == "==": return ['Foo實例對比:', ' 值: %s != %s' % (left.val, right.val)]
現(xiàn)在,在測試模塊使用
# test_foocompare.py內(nèi)容 class Foo(object): 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
運行這個測試模塊你可以看到conftest.py文件中定義的自定義輸出:
$ pytest -q test_foocompare.py
F
================================= FAILURES ================================= _______________________________ test_compare _______________________________
def test_compare():
f1 = Foo(1)
f2 = Foo(2)
> assert f1 == f2
E assert Foo實例對比:
E 值: 1 != 2
test_foocompare.py:11: AssertionError
1 failed in 0.12 seconds
高級斷言內(nèi)省
2.1版本新功能
報告有關失敗斷言的詳細信息是通過在運行之前重寫assert語句來實現(xiàn)的。 重寫的斷言語句將內(nèi)省信息放入斷言失敗消息中。pytest只重寫測試收集過程直接發(fā)現(xiàn)的測試模塊中的assert斷言,因此在支持模塊(非測試模塊)中的斷言,不會被重寫。
你可以在導入模塊前通過調(diào)用register_assert_rewrite手動啟用斷言重寫(比如可以在conftest.py這樣使用)。
注意
pytest通過使用導入hook方法寫入新的pyc文件來重寫測試模塊。 通常這種結(jié)構(gòu)比較清晰。 但是,如果你混亂導入,導入的hook方法可能會受到干擾。
如果是這種情況,您有兩種選擇:
通過將字符串PYTEST_DONT_REWRITE添加到其docstring來禁用特定模塊的重寫。
使用--assert = plain禁用所有模塊的重寫。
此外,如果無法寫入新的.pyc文件(如在只讀文件系統(tǒng)或zip文件中),重寫將無提示失敗。
有關進一步的信息,課參閱:本杰明彼得森寫的pytest的新斷言改寫的幕后故事。
版本2.1新功能:添加斷言重寫作為備用內(nèi)省技術。
版本2.1更改:引入--assert選項。 棄用--no-assert和--nomagic。
版本3.0版更改:刪除--no-assert和--nomagic選項。 刪除--assert = reinterp`選項。
老師微信:xiaowanzi02620[/cp]