如何使用 skip 和 xfail 處理無法成功的測試¶
您可以標記在特定平台上無法運行的測試函數,或您預期會失敗的測試函數,以便 pytest 可以相應地處理它們並呈現測試會話的摘要,同時保持測試套件綠色。
Skip 表示您預期您的測試只有在滿足某些條件時才會通過,否則 pytest 應完全跳過運行測試。常見的例子是在非 Windows 平台上跳過僅限 Windows 的測試,或跳過依賴於目前不可用的外部資源(例如資料庫)的測試。
Xfail 表示您預期測試會因某些原因而失敗。常見的例子是針對尚未實作的功能或尚未修復的錯誤進行測試。當預期會失敗(標記為 pytest.mark.xfail
)的測試通過時,它會被視為 xpass,並將在測試摘要中報告。
pytest
會分別計數和列出 skip 和 xfail 測試。預設情況下,不會顯示有關跳過/xfail 測試的詳細資訊,以避免輸出混亂。您可以使用 -r
選項來查看與測試進度中顯示的「簡短」字母相對應的詳細資訊
pytest -rxXs # show extra info on xfailed, xpassed, and skipped tests
有關 -r
選項的更多詳細資訊,可以透過運行 pytest -h
找到。
(請參閱 內建組態檔案選項)
跳過測試函數¶
跳過測試函數的最簡單方法是使用 skip
裝飾器標記它,該裝飾器可以傳遞一個可選的 reason
@pytest.mark.skip(reason="no way of currently testing this")
def test_the_unknown(): ...
或者,也可以透過調用 pytest.skip(reason)
函數在測試執行或設定期間命令式地跳過
def test_function():
if not valid_config():
pytest.skip("unsupported configuration")
當在導入時無法評估跳過條件時,命令式方法很有用。
也可以使用模組級別的 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
¶
如果您希望有條件地跳過某些內容,那麼您可以使用 skipif
來代替。以下是一個範例,說明如何在 Python 3.10 之前的直譯器上運行時跳過測試函數
import sys
@pytest.mark.skipif(sys.version_info < (3, 10), reason="requires python3.10 or higher")
def test_function(): ...
如果條件在收集期間評估為 True
,則將跳過測試函數,並且在使用 -rs
時,指定的理由將出現在摘要中。
您可以在模組之間共享 skipif
標記。考慮這個測試模組
# 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(): ...
您可以導入標記並在另一個測試模組中重複使用它
# test_myothermodule.py
from test_mymodule import minversion
@minversion
def test_anotherfunction(): ...
對於較大的測試套件,通常最好有一個檔案,您可以在其中定義標記,然後在整個測試套件中一致地應用這些標記。
或者,您可以使用 條件字串 而不是布林值,但它們不容易在模組之間共享,因此主要為了向後相容性而支援它們。
跳過類別或模組的所有測試函數¶
您可以在類別上使用 skipif
標記(與任何其他標記一樣)
@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
,則此標記將為該類別的每個測試方法產生跳過結果。
如果您想跳過模組的所有測試函數,您可以使用 pytestmark
全域變數
# test_module.py
pytestmark = pytest.mark.skipif(...)
如果將多個 skipif
裝飾器應用於測試函數,則只要任何一個跳過條件為真,就會跳過它。
跳過檔案或目錄¶
有時您可能需要跳過整個檔案或目錄,例如,如果測試依賴於特定於 Python 版本的特性,或包含您不希望 pytest 運行的程式碼。在這種情況下,您必須從收集範圍中排除檔案和目錄。有關更多資訊,請參閱 自訂測試收集。
因缺少導入依賴項而跳過¶
您可以透過在模組級別、測試內部或測試設定函數中使用 pytest.importorskip 來跳過因缺少導入而導致的測試。
docutils = pytest.importorskip("docutils")
如果在此處無法導入 docutils
,這將導致測試結果為跳過。您也可以根據程式庫的版本號跳過
docutils = pytest.importorskip("docutils", minversion="0.3")
版本將從指定模組的 __version__
屬性中讀取。
摘要¶
以下是在不同情況下如何在模組中跳過測試的快速指南
無條件跳過模組中的所有測試
pytestmark = pytest.mark.skip("all tests still WIP")
根據某些條件跳過模組中的所有測試
pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="tests for linux only")
如果缺少某些導入,則跳過模組中的所有測試
pexpect = pytest.importorskip("pexpect")
XFail:將測試函數標記為預期會失敗¶
您可以使用 xfail
標記來指示您預期測試會失敗
@pytest.mark.xfail
def test_function(): ...
此測試將運行,但失敗時不會報告回溯。相反,終端報告會將其列在「預期失敗」(XFAIL
) 或「意外通過」(XPASS
) 區段中。
或者,您也可以從測試內部或其設定函數中命令式地將測試標記為 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")
這兩個範例說明了您不想在模組級別檢查條件的情況,在這種情況下,條件將針對標記進行評估。
這將使 test_function
XFAIL
。請注意,在 pytest.xfail()
調用之後,不會執行其他程式碼,這與標記不同。這是因為它在內部透過引發已知的例外情況來實作。
condition
參數¶
如果測試僅在特定條件下預期會失敗,您可以將該條件作為第一個參數傳遞
@pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library")
def test_function(): ...
請注意,您也必須傳遞一個理由(請參閱 pytest.mark.xfail 上的參數描述)。
reason
參數¶
您可以使用 reason
參數指定預期失敗的動機
@pytest.mark.xfail(reason="known parser issue")
def test_function(): ...
raises
參數¶
如果您想更具體地說明測試失敗的原因,您可以在 raises
參數中指定單個例外或例外元組。
@pytest.mark.xfail(raises=RuntimeError)
def test_function(): ...
然後,如果測試因 raises
中未提及的例外而失敗,則會將其報告為常規失敗。
run
參數¶
如果測試應標記為 xfail 並如此報告,但不應執行,請將 run
參數設定為 False
@pytest.mark.xfail(run=False)
def test_function(): ...
這對於 xfail 那些會使直譯器崩潰且應稍後調查的測試特別有用。
strict
參數¶
預設情況下,XFAIL
和 XPASS
都不会使測試套件失敗。您可以透過將 strict
僅限關鍵字參數設定為 True
來變更此行為
@pytest.mark.xfail(strict=True)
def test_function(): ...
這將使此測試的 XPASS
(「意外通過」)結果導致測試套件失敗。
您可以使用 xfail_strict
ini 選項變更 strict
參數的預設值
[pytest]
xfail_strict=true
忽略 xfail¶
透過在命令列中指定
pytest --runxfail
您可以強制運行和報告標記為 xfail
的測試,就像它根本沒有被標記一樣。這也會導致 pytest.xfail()
不產生任何效果。
範例¶
這是一個簡單的測試檔案,其中包含多種用法
from __future__ import annotations
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 選項運行它會產生以下輸出
! 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 ============================
使用 parametrize 的 Skip/xfail¶
在使用 parametrize 時,可以將 skip 和 xfail 等標記應用於個別測試實例
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