pytest 測試輸出和結(jié)果-使用skip和xfail處理無法成功的測試

2022-03-21 15:23 更新

您可以標(biāo)記不能在某些平臺上運(yùn)行的測試函數(shù),或者您預(yù)計(jì)會失敗的測試函數(shù),以便pytest可以相應(yīng)地處理它們,并顯示測試會話的摘要,同時保持測試套件為綠色。

?skip?意味著您希望您的測試只有在滿足某些條件時才會通過,否則pytest應(yīng)該完全跳過運(yùn)行測試。常見的例子是跳過非windows平臺上的僅限windows的測試,或者跳過依賴于當(dāng)前不可用的外部資源(例如數(shù)據(jù)庫)的測試。

?xfail?意味著您預(yù)期測試由于某種原因會失敗。一個常見的例子是對尚未實(shí)現(xiàn)的特性或尚未修復(fù)的bug進(jìn)行測試。當(dāng)測試通過時,盡管預(yù)期會失敗(標(biāo)記為?pytest.mark.xfail?),它是一個?xpass?,并將在測試總結(jié)中報告。

Pytest分別計(jì)數(shù)和列出?skip?和?xfail?測試。默認(rèn)情況下,不顯示有關(guān)?skip?/?xfailed?測試的詳細(xì)信息,以避免輸出混亂。你可以使用?-r?選項(xiàng)來查看測試進(jìn)度中顯示的?short?字母對應(yīng)的詳細(xì)信息:

pytest -rxXs  # show extra info on xfailed, xpassed, and skipped tests

通過運(yùn)行 ?pytest -h? 可以找到有關(guān) ?-r選項(xiàng)的更多詳細(xì)信息。

跳過測試功能

跳過一個測試函數(shù)的最簡單的方法是用?skip?裝飾器來標(biāo)記它,它可以傳遞一個可選的原因:

@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown():
    ...

或者,也可以通過調(diào)用 ?pytest.skip(reason)? 函數(shù)在測試執(zhí)行或設(shè)置期間強(qiáng)制跳過:

def test_function():
    if not valid_config():
        pytest.skip("unsupported configuration")

當(dāng)在導(dǎo)入期間無法評估跳過條件時,命令式方法很有用。

也可以在模塊級別使用 ?pytest.skip(reason, allow_module_level=True)? 跳過整個模塊:

import sys
import pytest

if not sys.platform.startswith("win"):
    pytest.skip("skipping windows-only tests", allow_module_level=True)

skipif

如果您希望有條件地跳過某些內(nèi)容,則可以改用 ?skipif?。 以下是在 Python3.10 之前的解釋器上運(yùn)行時標(biāo)記要跳過的測試函數(shù)的示例:

import sys


@pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher")
def test_function():
    ...

如果在收集過程中條件評估為 ?True?,則將跳過測試函數(shù),使用 ?-rs? 時會在摘要中顯示指定的原因。

您可以在模塊之間共享 ?skipif標(biāo)記。 考慮這個測試模塊:

# content of test_mymodule.py
import mymodule

minversion = pytest.mark.skipif(
    mymodule.__versioninfo__ < (1, 1), reason="at least mymodule-1.1 required"
)


@minversion
def test_function():
    ...

您可以導(dǎo)入標(biāo)記并在另一個測試模塊中重用它:

# test_myothermodule.py
from test_mymodule import minversion


@minversion
def test_anotherfunction():
    ...

對于較大的測試套件,最好使用一個文件來定義標(biāo)記,然后在整個測試套件中始終如一地應(yīng)用這些標(biāo)記。

或者,您可以使用條件字符串而不是布爾值,但它們不能在模塊之間輕松共享,因此主要出于向后兼容性的原因支持它們。

跳過類或模塊的所有測試功能

您可以在類上使用 ?skipif標(biāo)記(與任何其他標(biāo)記一樣):

@pytest.mark.skipif(sys.platform == "win32", reason="does not run on windows")
class TestPosixCalls:
    def test_function(self):
        "will not be setup or run under 'win32' platform"

如果條件為?True?,該標(biāo)記將為該類的每個測試方法產(chǎn)生一個跳過結(jié)果。

如果你想跳過一個模塊的所有測試函數(shù),你可以使用全局?pytestmark?:

# test_module.py
pytestmark = pytest.mark.skipif(...)

如果將多個 ?skipif ?裝飾器應(yīng)用于測試函數(shù),則如果任何跳過條件為?true?,它將被跳過。

跳過文件或目錄

有時您可能需要跳過整個文件或目錄,例如,如果測試依賴于 Python 版本特定的功能或包含您不希望 pytest 運(yùn)行的代碼。 在這種情況下,您必須從集合中排除文件和目錄。

跳過缺少的導(dǎo)入依賴項(xiàng)

您可以通過在模塊級別、測試或測試設(shè)置函數(shù)中使用 ?pytest.importorskip? 來跳過缺少導(dǎo)入的測試。

docutils = pytest.importorskip("docutils")

如果此處無法導(dǎo)入 ?docutils?,這將導(dǎo)致測試的跳過結(jié)果。 您也可以根據(jù)庫的版本號跳過:

docutils = pytest.importorskip("docutils", minversion="0.3")

版本將從指定模塊的 ?__version__? 屬性中讀取。

概述

以下是有關(guān)如何在不同情況下跳過模塊中的測試的快速指南:

  • 無條件跳過模塊中的所有測試:

pytestmark = pytest.mark.skip("all tests still WIP")

  • 根據(jù)某些條件跳過模塊中的所有測試:

pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="tests for linux only")

  • 如果缺少某些導(dǎo)入,則跳過模塊中的所有測試:

pexpect = pytest.importorskip("pexpect")

XFail:將測試功能標(biāo)記為預(yù)期失敗

您可以使用 ?xfail標(biāo)記來指示您希望測試失敗:

@pytest.mark.xfail
def test_function():
    ...

此測試將運(yùn)行,但失敗時不會報告回溯。 相反,終端報告會將其列在“預(yù)期失敗”(?XFAIL?)或“意外通過”(?XPASS?)部分。

或者,您也可以在測試或其設(shè)置函數(shù)中強(qiáng)制將測試標(biāo)記為 ?XFAIL?:

def test_function():
    if not valid_config():
        pytest.xfail("failing configuration (but should work)")
def test_function2():
    import slow_module

    if slow_module.slow_function():
        pytest.xfail("slow_module taking too long")

這兩個例子說明了您不希望在模塊級檢查條件的情況,也就是當(dāng)一個條件將被用于標(biāo)記時。

這將使?test_function XFAIL?。請注意,在調(diào)用?pytest.xfail()?之后不會執(zhí)行其他代碼,這與標(biāo)記不同。這是因?yàn)樗峭ㄟ^引發(fā)已知異常在內(nèi)部實(shí)現(xiàn)的。

condition參數(shù)

如果測試只在特定條件下失敗,您可以將該條件作為第一個參數(shù)傳遞:

@pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library")
def test_function():
    ...

reason參數(shù)

你可以用?reason?參數(shù)指定預(yù)期失敗的動機(jī):

@pytest.mark.xfail(reason="known parser issue")
def test_function():
    ...

raises參數(shù)

如果您想更具體地了解測試失敗的原因,您可以在 ?raises參數(shù)中指定單個異?;虍惓TM:

@pytest.mark.xfail(raises=RuntimeError)
def test_function():
    ...

然后,如果測試失敗并出現(xiàn) ?raises中未提及的異常,則該測試將被報告為常規(guī)失敗。

run參數(shù)

如果測試應(yīng)標(biāo)記為 ?xfail并報告為 ?xfail?,但甚至不應(yīng)執(zhí)行,請將 ?run ?參數(shù)設(shè)置為 ?False?:

@pytest.mark.xfail(run=False)
def test_function():
    ...

這對于導(dǎo)致解釋器崩潰的失敗測試特別有用,應(yīng)該稍后進(jìn)行調(diào)查。

strict參數(shù)

默認(rèn)情況下,?XFAIL和 ?XPASS都不會使測試套件失敗。 您可以通過將 ?strict keyword-only? 參數(shù)設(shè)置為 ?True來更改此設(shè)置:

@pytest.mark.xfail(strict=True)
def test_function():
    ...

這將使該測試的 ?XPASS?(“意外通過”)結(jié)果無法通過測試套件。

您可以使用 ?xfail_strict ini? 選項(xiàng)更改 ?strict參數(shù)的默認(rèn)值:

[pytest]
xfail_strict=true

忽略xfail

通過在命令行上指定:

pytest --runxfail

您可以強(qiáng)制運(yùn)行和報告帶有 ?xfail標(biāo)記的測試,就好像它根本沒有標(biāo)記一樣。 這也會導(dǎo)致 ?pytest.xfail()? 不起作用。

例如,這是一個具有多種用途的簡單測試文件:

import pytest

xfail = pytest.mark.xfail


@xfail
def test_hello():
    assert 0


@xfail(run=False)
def test_hello2():
    assert 0


@xfail("hasattr(os, 'sep')")
def test_hello3():
    assert 0


@xfail(reason="bug 110")
def test_hello4():
    assert 0


@xfail('pytest.__version__[0] != "17"')
def test_hello5():
    assert 0


def test_hello6():
    pytest.xfail("reason")


@xfail(raises=IndexError)
def test_hello7():
    x = []
    x[1] = 1

使用 ?report-on-xfail? 選項(xiàng)運(yùn)行它會給出以下輸出:

! pytest -rx xfail_demo.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR/example
collected 7 items

xfail_demo.py xxxxxxx                                                [100%]

========================= short test summary info ==========================
XFAIL xfail_demo.py::test_hello
XFAIL xfail_demo.py::test_hello2
  reason: [NOTRUN]
XFAIL xfail_demo.py::test_hello3
  condition: hasattr(os, 'sep')
XFAIL xfail_demo.py::test_hello4
  bug 110
XFAIL xfail_demo.py::test_hello5
  condition: pytest.__version__[0] != "17"
XFAIL xfail_demo.py::test_hello6
  reason: reason
XFAIL xfail_demo.py::test_hello7
============================ 7 xfailed in 0.12s ============================

使用參數(shù)化skip/xfail

使用參數(shù)化時,可以將skipxfail等標(biāo)記應(yīng)用于單個測試實(shí)例:

import sys
import pytest


@pytest.mark.parametrize(
    ("n", "expected"),
    [
        (1, 2),
        pytest.param(1, 0, marks=pytest.mark.xfail),
        pytest.param(1, 3, marks=pytest.mark.xfail(reason="some bug")),
        (2, 3),
        (3, 4),
        (4, 5),
        pytest.param(
            10, 11, marks=pytest.mark.skipif(sys.version_info >= (3, 0), reason="py2k")
        ),
    ],
)
def test_increment(n, expected):
    assert n + 1 == expected


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號