Pytest官方教程-04-斷言的編寫和報(bào)告

目錄:

  1. 安裝及入門
  2. 使用和調(diào)用方法
  3. 原有TestSuite使用方法
  4. 斷言的編寫和報(bào)告
  5. Pytest fixtures:清晰 模塊化 易擴(kuò)展
  6. 使用Marks標(biāo)記測(cè)試用例
  7. Monkeypatching/對(duì)模塊和環(huán)境進(jìn)行Mock
  8. 使用tmp目錄和文件
  9. 捕獲stdout及stderr輸出
  10. 捕獲警告信息
  11. 模塊及測(cè)試文件中集成doctest測(cè)試
  12. skip及xfail: 處理不能成功的測(cè)試用例
  13. Fixture方法及測(cè)試用例的參數(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. 對(duì)象(Objects)
    6. 特殊變量(Special Variables)
    7. 環(huán)境變量(Environment Variables)
    8. 配置選項(xiàng)(Configuration Options)
  23. 優(yōu)質(zhì)集成實(shí)踐
  24. 片狀測(cè)試
  25. Pytest導(dǎo)入機(jī)制及sys.path/PYTHONPATH
  26. 配置選項(xiàng)
  27. 示例及自定義技巧
  28. Bash自動(dòng)補(bǔ)全設(shè)置

斷言的編寫和報(bào)告

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

pytest允許你使用標(biāo)準(zhǔn)的Pythonassert斷言語(yǔ)句來(lái)驗(yàn)證測(cè)試中的期望結(jié)果和實(shí)際結(jié)果。 例如,你可以編寫以下內(nèi)容:

# test_assert1.py文件內(nèi)容
def f():
    return 3

def test_function():
    assert f() == 4

來(lái)斷言你的函數(shù)返回一個(gè)特定的值。 如果此斷言失敗,你將看到函數(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測(cè)試失敗報(bào)告示例)。 你可以在不使用繁瑣的Python慣用構(gòu)造樣板代碼的同時(shí),不丟失斷言失敗的對(duì)比信息(內(nèi)省信息)。

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

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

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

異常斷言

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

import pytest

def test_zero_division():
    with pytest.raises(ZeroDivisionError):
        1 / 0

如果需要訪問(wèn)實(shí)際的異常信息,你可以使用:

def test_recursion_depth():
    with pytest.raises(RuntimeError) as excinfo:
        def f():
            f()
        f()
    assert 'maximum recursion' in str(excinfo.value)

excinfo是一個(gè)ExceptionInfo實(shí)例,它是實(shí)際異常的裝飾器。 其主要屬性有.type.value.traceback三種
版本3.0已修改
在上下文管理器中,你可以使用參數(shù)message來(lái)指定自定義失敗信息:

>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
...     pass
... Failed: Expecting ZeroDivisionError

如果你想編寫適用于Python 2.4的測(cè)試代碼,你還可以使用其他兩種方法來(lái)測(cè)試預(yù)期的異常:

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

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

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

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

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

此外,上下文管理器表單接受match關(guān)鍵字參數(shù)來(lái)測(cè)試正則表達(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ù)來(lái)進(jìn)行匹配一致。 因此在上面的例子中,match ='123'不會(huì)引發(fā)異常。

警示斷言

2.8版本新增
你可以使用pytest.warns檢查代碼是否引發(fā)了特定警告。

使用上下文對(duì)比

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)行這個(gè)模塊后

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

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

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

自定義斷言對(duì)比信息

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

如果沒有自定義對(duì)比信息,則返回None,否則返回一列字符串。 字符串將由換行符連接,但字符串中的任何換行符都將被轉(zhuǎn)義。 請(qǐng)注意,除第一行外的所有行都將略微縮進(jìn),目的是將第一行作為摘要。
參數(shù): config(pytest.config.Config* - pytest config 對(duì)象
例如,在conftest.py文件中添加以下鉤子方法,可以為Foo對(duì)象提供了附加對(duì)比信息:

# 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實(shí)例對(duì)比:',
                '   值: %s != %s' % (left.val, right.val)]

現(xiàn)在,在測(cè)試模塊使用

# 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)行這個(gè)測(cè)試模塊你可以看到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實(shí)例對(duì)比:
E            值: 1 != 2

test_foocompare.py:11: AssertionError
1 failed in 0.12 seconds

高級(jí)斷言內(nèi)省

2.1版本新功能
報(bào)告有關(guān)失敗斷言的詳細(xì)信息是通過(guò)在運(yùn)行之前重寫assert語(yǔ)句來(lái)實(shí)現(xiàn)的。 重寫的斷言語(yǔ)句將內(nèi)省信息放入斷言失敗消息中。 pytest只重寫測(cè)試收集過(guò)程直接發(fā)現(xiàn)的測(cè)試模塊中的assert斷言,因此在支持模塊(非測(cè)試模塊)中的斷言,不會(huì)被重寫。

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

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

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

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

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

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