Pytest官方教程-04-斷言的編寫和報告

目錄:

  1. 安裝及入門
  2. 使用和調(diào)用方法
  3. 原有TestSuite使用方法
  4. 斷言的編寫和報告
  5. Pytest fixtures:清晰 模塊化 易擴(kuò)展
  6. 使用Marks標(biāo)記測試用例
  7. Monkeypatching/對模塊和環(huán)境進(jìn)行Mock
  8. 使用tmp目錄和文件
  9. 捕獲stdout及stderr輸出
  10. 捕獲警告信息
  11. 模塊及測試文件中集成doctest測試
  12. skip及xfail: 處理不能成功的測試用例
  13. Fixture方法及測試用例的參數(shù)化
  14. 緩存: 使用跨執(zhí)行狀態(tài)
  15. unittest.TestCase支持
  16. 運(yùn)行Nose用例
  17. 經(jīng)典xUnit風(fēng)格的setup/teardown
  18. 安裝和使用插件
  19. 插件編寫
  20. 編寫鉤子(hook)方法
  21. 運(yùn)行日志
  22. API參考
    1. 方法(Functions)
    2. 標(biāo)記(Marks)
    3. 鉤子(Hooks)
    4. 裝置(Fixtures)
    5. 對象(Objects)
    6. 特殊變量(Special Variables)
    7. 環(huán)境變量(Environment Variables)
    8. 配置選項(Configuration Options)
  23. 優(yōu)質(zhì)集成實踐
  24. 片狀測試
  25. Pytest導(dǎo)入機(jī)制及sys.path/PYTHONPATH
  26. 配置選項
  27. 示例及自定義技巧
  28. Bash自動補(bǔ)全設(shè)置

斷言的編寫和報告

使用assert語句進(jìn)行斷言

pytest允許你使用標(biāo)準(zhǔn)的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)用,屬性,比較以及二元和一元運(yùn)算符子表達(dá)式的值 (參考:pytest執(zhí)行Python測試失敗報告示例)。 你可以在不使用繁瑣的Python慣用構(gòu)造樣板代碼的同時,不丟失斷言失敗的對比信息(內(nèi)省信息)。

當(dāng)然,你也可以像下面所示,指定斷言失敗的返回消息:

assert a % 2 == 0, "值為奇數(shù),應(yīng)為偶數(shù)"

這樣將不會斷言失敗對比信息(內(nèi)省信息),而只簡單地在追溯信息中顯示你指定的失敗返回信息。
有關(guān)斷言內(nèi)省的更多信息,請參閱高級斷言內(nèi)省。

異常斷言

你可以像如下所示,使用pytest.raises作為上下文管理器來進(jìn)行異常斷言:

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的測試代碼,你還可以使用其他兩種方法來測試預(yù)期的異常:

pytest.raises(ExpectedException, func, *args, **kwargs)
pytest.raises(ExpectedException, "func(*args, **kwargs)")

兩者都可以對帶任意參數(shù)的函數(shù),斷言是否出現(xiàn)了期望的異常:ExpectedException。 即使沒有異?;虺霈F(xiàn)了不同的異常,報告生成器也能輸出一些有用的斷言信息。

注意,也可以為pytest.mark.xfail指定一個“raises”參數(shù),當(dāng)引發(fā)異常時標(biāo)記用例失?。?/p>

@pytest.mark.xfail(raises=IndexError)
def test_f():
    f()

對于你在代碼中故意設(shè)置的異常,使用pytest.raises斷言更加好用,而將@ pytest.mark.xfail與check函數(shù)一起使用對于已知未修復(fù)或依賴中的bug會更好。

此外,上下文管理器表單接受match關(guān)鍵字參數(shù)來測試正則表達(dá)式匹配中的異常(如unittest中的TestCase.assertRaisesRegexp方法):

import pytest

def myfunc():
    raise ValueError("Exception 123 raised")

def test_match():
    with pytest.raises(ValueError, match=r'.* 123 .*'):
        myfunc()

match變量后的正則表達(dá)式與使用re.search函數(shù)來進(jìn)行匹配一致。 因此在上面的例子中,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

當(dāng)你運(yùn)行這個模塊后

$ 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 =========================

對大量用例進(jìn)行了特定對比:

  • 長字符串?dāng)嘌裕猴@示上下文差異
  • 長序列斷言:顯示第一個失敗的索引
  • 字典斷言:顯示不同的key-value對
    有關(guān)更多示例,請參閱報告樣例。

自定義斷言對比信息

可以通過實現(xiàn)hook方法pytest_assertrepr_compare來在斷言結(jié)果中添加你自己的詳細(xì)說明信息。
pytest_assertrepr_compare(config, op, left, right) [源碼]
返回失敗斷言表達(dá)式中的對比信息。

如果沒有自定義對比信息,則返回None,否則返回一列字符串。 字符串將由換行符連接,但字符串中的任何換行符都將被轉(zhuǎn)義。 請注意,除第一行外的所有行都將略微縮進(jì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

運(yùn)行這個測試模塊你可以看到conftest.py文件中定義的自定義輸出:

$ pytest -q test_foocompare.py
F                                                                    [100%]
================================= 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版本新功能
報告有關(guān)失敗斷言的詳細(xì)信息是通過在運(yùn)行之前重寫assert語句來實現(xiàn)的。 重寫的斷言語句將內(nèi)省信息放入斷言失敗消息中。 pytest只重寫測試收集過程直接發(fā)現(xiàn)的測試模塊中的assert斷言,因此在支持模塊(非測試模塊)中的斷言,不會被重寫。

你可以在導(dǎo)入模塊前通過調(diào)用register_assert_rewrite手動啟用斷言重寫(比如可以在conftest.py這樣使用)。

注意
pytest通過使用導(dǎo)入hook方法寫入新的pyc文件來重寫測試模塊。 通常這種結(jié)構(gòu)比較清晰。 但是,如果你混亂導(dǎo)入,導(dǎo)入的hook方法可能會受到干擾。
如果是這種情況,您有兩種選擇:
通過將字符串PYTEST_DONT_REWRITE添加到其docstring來禁用特定模塊的重寫。
使用--assert = plain禁用所有模塊的重寫。
此外,如果無法寫入新的.pyc文件(如在只讀文件系統(tǒng)或zip文件中),重寫將無提示失敗。
有關(guān)進(jìn)一步的信息,課參閱:本杰明彼得森寫的pytest的新斷言改寫的幕后故事。

版本2.1新功能:添加斷言重寫作為備用內(nèi)省技術(shù)。
版本2.1更改:引入--assert選項。 棄用--no-assert--nomagic
版本3.0版更改:刪除--no-assert和--nomagic選項。 刪除--assert = reinterp`選項。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容