目錄html
往期索引:http://www.javashuo.com/article/p-wpvwlyap-bn.htmlpython
實際工做中,測試用例的執行可能會依賴於一些外部條件,例如:只能運行在某個特定的操做系統(Windows
),或者咱們自己指望它們測試失敗,例如:被某個已知的Bug
所阻塞;若是咱們能爲這些用例提早打上標記,那麼pytest
就相應地預處理它們,並提供一個更加準確的測試報告;shell
在這種場景下,經常使用的標記有:windows
skip
:只有當某些條件獲得知足時,才執行測試用例,不然跳過整個測試用例的執行;例如,在非Windows
平臺上跳過只支持Windows
系統的用例;xfail
:由於一個確切的緣由,咱們知道這個用例會失敗;例如,對某個未實現的功能的測試,或者阻塞於某個已知Bug
的測試;pytest
默認不顯示skip
和xfail
用例的詳細信息,咱們能夠經過-r
選項來自定義這種行爲;api
一般,咱們使用一個字母做爲一種類型的表明,具體的規則以下:數組
(f)ailed, (E)rror, (s)kipped, (x)failed, (X)passed, (p)assed, (P)assed with output, (a)ll except passed(p/P), or (A)ll
例如,顯示結果爲XFAIL
、XPASS
和SKIPPED
的用例:session
pytest -rxXs
更多細節能夠參考:二、使用和調用 -- 總結報告xss
@pytest.mark.skip
裝飾器跳過執行某個用例最簡單的方式就是使用@pytest.mark.skip
裝飾器,而且能夠設置一個可選參數reason
,代表跳過的緣由;模塊化
@pytest.mark.skip(reason="no way of currently testing this") def test_the_unknown(): ...
pytest.skip
方法若是咱們想在測試執行期間(也能夠在SetUp/TearDown
期間)強制跳事後續的步驟,能夠考慮pytest.skip()
方法,它一樣能夠設置一個參數msg
,代表跳過的緣由;測試
def test_function(): if not valid_config(): pytest.skip("unsupported configuration")
另外,咱們還能夠爲其設置一個布爾型的參數allow_module_level
(默認是False
),代表是否容許在模塊中調用這個方法,若是置爲True
,則跳過模塊中剩餘的部分;
例如,在Windows
平臺下,不測試這個模塊:
import sys import pytest if not sys.platform.startswith("win"): pytest.skip("skipping windows-only tests", allow_module_level=True)
注意:
當在用例中設置
allow_module_level
參數時,並不會生效;def test_one(): pytest.skip("跳出", allow_module_level=True) def test_two(): assert 1也就是說,在上述示例中,並不會跳過
test_two
用例;
@pytest.mark.skipif
裝飾器若是咱們想有條件的跳過某些測試用例的執行,可使用@pytest.mark.skipif
裝飾器;
例如,當python
的版本小於3.6
時,跳過用例:
import sys @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_function(): ...
咱們也能夠在兩個模塊之間共享pytest.mark.skipif
標記;
例如,咱們在test_module.py
中定義了minversion
,代表當python
的最低支持版本:
# src/chapter-10/test_module.py import sys import pytest minversion = pytest.mark.skipif(sys.version_info < (3, 8), reason='請使用 python 3.8 或者更高的版本。') @minversion def test_one(): assert True
而且,在test_other_module.py
中引入了minversion
:
# src/chapter-10/test_other_module.py from test_module import minversion @minversion def test_two(): assert True
如今,咱們來執行這兩個用例(當前虛擬環境的python
版本爲3.7.3
):
λ pipenv run pytest -rs -k 'module' src/chapter-10/ ================================ test session starts ================================= platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0 rootdir: D:\Personal Files\Projects\pytest-chinese-doc collected 2 items src\chapter-10\test_module.py s [ 50%] src\chapter-10\test_other_module.py s [100%] ============================== short test summary info =============================== SKIPPED [1] src\chapter-10\test_module.py:29: 請使用 python 3.8 或者更高的版本。 SKIPPED [1] src\chapter-10\test_other_module.py:26: 請使用 python 3.8 或者更高的版本。 ================================= 2 skipped in 0.03s =================================
能夠看到,minversion
在兩個測試模塊中都生效了;
所以,在大型的測試項目中,能夠在一個文件中定義全部的執行條件,須要時再引入到模塊中;
另外,須要注意的是,當一個用例指定了多個skipif
條件時,只需知足其中一個,就能夠跳過這個用例的執行;
注意:不存在
pytest.skipif()
的方法;
pytest.importorskip
方法當引入某個模塊失敗時,咱們一樣能夠跳事後續部分的執行;
docutils = pytest.importorskip("docutils")
咱們也能夠爲其指定一個最低知足要求的版本,判斷的依據是檢查引入模塊的__version__
屬性:
docutils = pytest.importorskip("docutils", minversion="0.3")
咱們還能夠再爲其指定一個reason
參數,代表跳過的緣由;
咱們注意到
pytest.importorskip
和pytest.skip(allow_module_level=True)
均可以在模塊的引入階段跳過剩餘部分;實際上,在源碼中它們拋出的都是一樣的異常:# pytest.skip(allow_module_level=True) raise Skipped(msg=msg, allow_module_level=allow_module_level)# pytest.importorskip() raise Skipped(reason, allow_module_level=True) from None只是
importorskip
額外增長了minversion
參數:# _pytest/outcomes.py if minversion is None: return mod verattr = getattr(mod, "__version__", None) if minversion is not None: if verattr is None or Version(verattr) < Version(minversion): raise Skipped( "module %r has __version__ %r, required is: %r" % (modname, verattr, minversion), allow_module_level=True, )從中咱們也證明了,它實際檢查的是模塊的
__version__
屬性;因此,對於通常場景下,使用下面的方法能夠實現一樣的效果:
try: import docutils except ImportError: pytest.skip("could not import 'docutils': No module named 'docutils'", allow_module_level=True)
在類上應用@pytest.mark.skip
或@pytest.mark.skipif
:
# src/chapter-10/test_skip_class.py import pytest @pytest.mark.skip("做用於類中的每個用例,因此 pytest 共收集到兩個 SKIPPED 的用例。") class TestMyClass(): def test_one(self): assert True def test_two(self): assert True
在模塊中定義pytestmark
變量(推薦):
# src/chapter-10/test_skip_module.py import pytest pytestmark = pytest.mark.skip('做用於模塊中的每個用例,因此 pytest 共收集到兩個 SKIPPED 的用例。') def test_one(): assert True def test_two(): assert True
或者,在模塊中調用pytest.skip
方法,並設置allow_module_level=True
:
# src/chapter-10/test_skip_module.py import pytest pytest.skip('在用例收集階段就已經跳出了,因此不會收集到任何用例。', allow_module_level=True) def test_one(): assert True def test_two(): assert True
經過在conftest.py
中配置collect_ignore_glob
項,能夠在用例的收集階段跳過指定的文件和目錄;
例如,跳過當前測試目錄中文件名匹配test_*.py
規則的文件和config
的子文件夾sub
中的文件:
collect_ignore_glob = ['test*.py', 'config/sub']
更多細節能夠參考:https://docs.pytest.org/en/5.1.3/example/pythoncollection.html#customizing-test-collection
pytest.mark.skip |
pytest.mark.skipif |
pytest.skip |
pytest.importorskip |
conftest.py |
|
---|---|---|---|---|---|
用例 | @pytest.mark.skip() |
@pytest.mark.skipif() |
pytest.skip(msg='') |
/ | / |
類 | @pytest.mark.skip() |
@pytest.mark.skipif() |
/ | / | / |
模塊 | pytestmark = pytest.mark.skip() |
pytestmark = pytest.mark.skipif() |
pytest.skip(allow_module_level=True) |
pytestmark = pytest.importorskip() |
/ |
文件或目錄 | / | / | / | / | collect_ignore_glob |
咱們可使用@pytest.mark.xfail
標記用例,表示指望這個用例執行失敗;
用例會正常執行,只是失敗時再也不顯示堆棧信息,最終的結果有兩個:用例執行失敗時(XFAIL
:符合預期的失敗)、用例執行成功時(XPASS
:不符合預期的成功)
另外,咱們也能夠經過pytest.xfail
方法在用例執行過程當中直接標記用例結果爲XFAIL
,並跳過剩餘的部分:
def test_function(): if not valid_config(): pytest.xfail("failing configuration (but should work)")
一樣能夠爲pytest.xfail
指定一個reason
參數,代表緣由;
下面咱們來重點看一下@pytest.mark.xfail
的用法:
condition
位置參數,默認值爲None
和@pytest.mark.skipif
同樣,它也能夠接收一個python
表達式,代表只有知足條件時才標記用例;
例如,只在pytest 3.6
版本以上標記用例:
@pytest.mark.xfail(sys.version_info >= (3, 6), reason="python3.6 api changes") def test_function(): ...
reason
關鍵字參數,默認值爲None
能夠指定一個字符串,代表標記用例的緣由;
strict
關鍵字參數,默認值爲False
當strict=False
時,若是用例執行失敗,結果標記爲XFAIL
,表示符合預期的失敗;若是用例執行成功,結果標記爲XPASS
,表示不符合預期的成功;
當strict=True
時,若是用例執行成功,結果將標記爲FAILED
,而再也不是XPASS
了;
咱們也能夠在pytest.ini
文件中配置:
[pytest] xfail_strict=true
raises
關鍵字參數,默認值爲None
能夠指定爲一個異常類或者多個異常類的元組,代表咱們指望用例上報指定的異常;
若是用例的失敗不是由於所指望的異常致使的,pytest
將會把測試結果標記爲FAILED
;
run
關鍵字參數,默認值爲True
:
當run=False
時,pytest
不會再執行測試用例,直接將結果標記爲XFAIL
;
咱們如下表來總結不一樣參數組合對測試結果的影響(其中xfail = pytest.mark.xfail
):
@xfail() |
@xfail(strict=True) |
@xfail(raises=IndexError) |
@xfail(strict=True, raises=IndexError) |
@xfail(..., run=False) |
|
---|---|---|---|---|---|
用例測試成功 | XPASS |
FAILED |
XPASS |
FAILED |
XFAIL |
用例測試失敗,上報AssertionError |
XFAIL |
XFAIL |
FAILED |
FAILED |
XFAIL |
用例上報IndexError |
XFAIL |
XFAIL |
XFAIL |
XFAIL |
XFAIL |
xfail
標記咱們能夠經過命令行選項pytest --runxfail
來去使能xfail
標記,使這些用例變成正常執行的用例,彷彿沒有被標記過同樣:
一樣,pytest.xfail()
方法也將會失效;
pytest.param
方法pytest.param
方法可用於爲@pytest.mark.parametrize
或者參數化的fixture
指定一個具體的實參,它有一個關鍵字參數marks
,能夠接收一個或一組標記,用於標記這輪測試的用例;
咱們如下面的例子來講明:
# src/chapter-10/test_params.py import pytest import sys @pytest.mark.parametrize( ('n', 'expected'), [(2, 1), pytest.param(2, 1, marks=pytest.mark.xfail(), id='XPASS'), pytest.param(0, 1, marks=pytest.mark.xfail(raises=ZeroDivisionError), id='XFAIL'), pytest.param(1, 2, marks=pytest.mark.skip(reason='無效的參數,跳過執行')), pytest.param(1, 2, marks=pytest.mark.skipif(sys.version_info <= (3, 8), reason='請使用3.8及以上版本的python。'))]) def test_params(n, expected): assert 2 / n == expected
執行:
λ pipenv run pytest -rA src/chapter-10/test_params.py ================================ test session starts ================================= platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0 rootdir: D:\Personal Files\Projects\pytest-chinese-doc collected 5 items src\chapter-10\test_params.py .Xxss [100%] ======================================= PASSES ======================================= ============================== short test summary info =============================== PASSED src/chapter-10/test_params.py::test_params[2-1] SKIPPED [1] src\chapter-10\test_params.py:26: 無效的參數,跳過執行 SKIPPED [1] src\chapter-10\test_params.py:26: 請使用3.8及以上版本的python。 XFAIL src/chapter-10/test_params.py::test_params[XFAIL] XPASS src/chapter-10/test_params.py::test_params[XPASS] ================= 1 passed, 2 skipped, 1 xfailed, 1 xpassed in 0.08s =================
關於參數化的fixture
的細節能夠參考:四、fixtures:明確的、模塊化的和可擴展的 -- 在參數化的fixture中標記用例