Pytest學習筆記4-assert斷言

前言

pytest作為單元測試框架,自然少不了斷言功能,用過unittest的人都知道,在unittest中有豐富的斷言方法,比如assertEqual()、assertIn()、assertTrue()、assertIs()等等,而在pytest中,並沒有提供特殊的斷言方法,而是直接使用python自帶的關鍵字assert來進行斷言操作。

下面我們就通過一些🌰來看看在pytest中是如何進行斷言操作的吧

常用斷言

Pytest里的斷言實際上就是Python中的assert斷言方法,常用斷言方法如下:

  • assert xx :判斷 xx 為真
  • assert not xx :判斷 xx 不為真
  • assert a in b :判斷 b 包含 a
  • assert a == b :判斷 a 等於 b
  • assert a != b :判斷 a 不等於 b

舉個🌰:

import pytest


def test_demo1():
    a = 1
    assert a


def test_demo2():
    a = 0
    assert not a


def test_demo3():
    s = 'hello'
    assert 'h' in s


def test_demo4():
    a = 3
    assert a == 3


def test_demo5():
    a = 4
    assert a != 3


if __name__ == '__main__':
    pytest.main()

運行結果如下:

image-20210624144052915

如果想在異常的時候,能夠輸出一些提示資訊,可在直接在斷言後面加上提示資訊,如下:

import pytest

def test_demo6():
    a = 5
    assert a == 3, "兩者不相等"

運行結果:

image-20210624151716763

異常斷言

在實際測試的過程中,我們經常需要對特定異常進行斷言,可以使用 pytest.raises 作為上下文管理器,當拋出異常時可以獲取到對應的異常實例

舉個🌰:

import pytest


def test_zero_division():
    1 / 0


if __name__ == '__main__':
    pytest.main()

運行結果:

image-20210624144815922

可以看到,這裡程式異常了,所以我們需要捕獲並斷言異常。

斷言場景:斷言拋出的異常是否符合預期。

預期結果:ZeroDivisionError: division by zero,其中ZeroDivisionError為錯誤類型,division by zero為具體錯誤值。

斷言方式: 斷言異常的type和value值。

斷言程式碼如下

import pytest


def test_zero_division():
    with pytest.raises(ZeroDivisionError) as excinfo:
        1 / 0
    # 斷言異常類型 type
    assert excinfo.type == ZeroDivisionError
    # 斷言異常 value 值
    assert "division by zero" in str(excinfo.value)


if __name__ == '__main__':
    pytest.main()

excinfo作為異常資訊實例,擁有type 、value、.traceback等屬性

excinfo.value的值是元組,所以要轉成字元串

在上下文管理器的作用域中,raises程式碼必須是最後一行,否則,其後面的程式碼將不會執行

拓展:match

你也可以給pytest.raises()傳遞一個關鍵字參數match,來測試異常的字元串表示str(excinfo.value)是否符合給定的正則表達式(和unittest中的TestCase.assertRaisesRegexp方法類似):

import pytest


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


def test_match():
  	# pytest.raises()函數,
    # 可以用元組的形式傳遞參數,只需要觸發其中任意一個即可。
    # 通過match可以設置通過正則表達式匹配異常。
    with pytest.raises((ValueError, RuntimeError), match=r'.* 123 .*') as excinfo:
        func()
    assert 「123」 in str(excinfo.value)
    
if __name__ == '__main__':
    pytest.main()

拓展:檢查斷言裝飾器

pytest.mark.xfail()也可以接收一個raises參數,來判斷用例是否因為一個具體的異常而導致失敗:

@pytest.mark.xfail(raises=ZeroDivisionError)
def test_f():
    1 / 0

執行結果:

image-20210624160307993

如果test_f()觸發的異常類型和raises指定的異常類型一致,則用例被標記為xfailed

如果test_f()測試成功,用例的結果是xpassed,而不是passed

pytest.raises適用於檢查由程式碼故意引發的異常;而@pytest.mark.xfail()更適合用於記錄一些未修復的 Bug

參考

pytest-chinese-doc