1. 程式人生 > >pytest封神之路第四步 內建和自定義marker

pytest封神之路第四步 內建和自定義marker

可以通過命令列檢視所有marker,包括內建和自定義的 ```shell pytest --markers ``` # 內建marker 內建marker本文先講usefixtures 、filterwarnings 、skip 、skipif 、xfail這5個。引數化的marker我會寫在《pytest引數化》中,hook的marker我會寫在《pytest hook》中,外掛的marker(pytest-ordering、allure等)我會寫在《pytest外掛》中。當前只需知道有以上這些分類的marker即可,更多內容請關注後續文章。 ## usefixtures 如果我們只想把fixture注入到test中,test不直接訪問fixture的時候,就需要用到usefixtures。 示例,test需要一個臨時目錄,但是並不需要知道這個目錄具體路徑在哪 ```python # content of conftest.py import os import shutil import tempfile import pytest @pytest.fixture def cleandir(): old_cwd = os.getcwd() newpath = tempfile.mkdtemp() os.chdir(newpath) yield os.chdir(old_cwd) shutil.rmtree(newpath) ``` ```python # content of test_setenv.py import os import pytest @pytest.mark.usefixtures("cleandir") class TestDirectoryInit: def test_cwd_starts_empty(self): assert os.listdir(os.getcwd()) == [] with open("myfile", "w") as f: f.write("hello") def test_cwd_again_starts_empty(self): assert os.listdir(os.getcwd()) == [] ``` TestDirectoryInit的測試方法需要一個臨時目錄作為當前工作目錄,在類上新增`@pytest.mark.usefixtures("cleandir")`,類的方法不加fixture也能有"cleandir"的效果。 usefixtures可以新增多個fixture ```python @pytest.mark.usefixtures("cleandir", "anotherfixture") ``` usefixtures可以用在**pytestmark**,作用域是定義所在module的所有tests ```python pytestmark = pytest.mark.usefixtures("cleandir") ``` usefixtures也可以用在**pytest.ini**,作用域是整個專案的所有tests ```ini # content of pytest.ini [pytest] usefixtures = cleandir ``` 不過需要注意的是fixture函式本身是不能用usefixtures的,如果想要巢狀fixture,只能通過在fixture修飾的函式中,新增引數這種方式。 ## filterwarnings 過濾警告資訊。 示例,api_v1()丟擲了“api v1”的警告,test_one()函式使用filterwarnings過濾掉了 ```python import warnings def api_v1(): warnings.warn(UserWarning("api v1, should use functions from v2")) return 1 @pytest.mark.filterwarnings("ignore:api v1") def test_one(): assert api_v1() == 1 ``` 同樣可以新增到pytestmark和pytest.ini中。 ## skip 跳過,不測試。 示例,skip需要新增reason哦 ```python @pytest.mark.skip(reason="no way of currently testing this") def test_the_unknown(): ... ``` 不過,更實用的方式是呼叫pytest.skip(reason)函式,而不是用mark,這樣就可以用if判斷跳不跳 ```python def test_function(): if not valid_config(): pytest.skip("unsupported configuration") ``` allow_module_level 可以跳過整個module ```python import sys import pytest if not sys.platform.startswith("win"): pytest.skip("skipping windows-only tests", allow_module_level=True) ``` ## skipif if判斷跳不跳,還可以用skipif。 示例,如果Python版本小於3.6就跳過測試 ```python import sys @pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python3.6 or higher") def test_function(): ... ``` 如果想在summary中看到reason,需要新增-rs引數。 可以把skipif賦值給**變數**,然後直接引用變數,或者把變數import到其他module中使用 ```python # 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(): ... ``` ```python # test_myothermodule.py from test_mymodule import minversion @minversion def test_anotherfunction(): ... ``` skipif新增到class上,會跳過類中所有方法。 可以使用pytestmark跳過module內所有test ```python # test_module.py pytestmark = pytest.mark.skipif(...) ``` 如果function有多個skipif作用,只要有一個為True,就會跳過。 ## xfail 明知失敗,依然前行!不好意思跑偏了。xfail就是expected fail,預期失敗 ```python @pytest.mark.xfail def test_function(): ... ``` 執行後summary不會統計為"failed",會單獨列出來。如果結果失敗了,“expected to fail” (XFAIL);如果結果成功了,“unexpectedly passing” (XPASS)。但是**整個執行結果是”Tests passed“。** if判斷 ```python def test_function(): if not valid_config(): pytest.xfail("failing configuration (but should work)") ``` 值得注意的是,marker會繼續執行所有test程式碼,pytest.xfail()函式會丟擲異常,中斷執行後續程式碼 新增**condition**,判斷條件 ```python @pytest.mark.xfail(sys.platform == "win32", reason="bug in a 3rd party library") def test_function(): ... ``` 新增**reason**,理由 ```python @pytest.mark.xfail(reason="known parser issue") def test_function(): ... ``` 新增**raises**,丟擲異常/錯誤 ```python @pytest.mark.xfail(raises=RuntimeError) def test_function(): ... ``` 新增**run**,禁止執行 ```python @pytest.mark.xfail(run=False) def test_function(): ... ``` 新增**strict**,嚴格模式,即使xpass也會強制失敗,summary中有輸出資訊”[XPASS(strict)] “,測試結果為”Tests failed“。 ```python @pytest.mark.xfail(strict=True) def test_function(): ... ``` 斷言成功也強制失敗,確實夠強勢的! 可以在ini檔案中定義全域性strict ```ini [pytest] xfail_strict=true ``` 在命令列新增**--runxfail**,忽略xfail marker,相當於沒有新增這個標記的效果,該成功就成功,該失敗就失敗,再強勢也不虛,哈哈,惡靈退散。 ```shell pytest --runxfail ``` ```python pytest --runxfail ``` 再強勢也不虛,惡靈退散,哈哈。 # 自定義marker 通過註解自定義marker ```python # content of test_server.py import pytest @pytest.mark.webtest def test_send_http(): pass # perform some webtest test for your app def test_something_quick(): pass def test_another(): pass class TestClass: def test_method(self): pass ``` 在命令列通過`-m`指定執行mark打標的test ```shell $ pytest -v -m webtest ``` 也可以反選 ```shell $ pytest -v -m "not webtest" ``` 但是,這樣定義的marker是**未註冊**的!在執行後會警告,PytestUnknownMarkWarning。如果添加了命令列引數`--strict-markers `,未註冊的marker會**報錯**。 可以在pytest.ini檔案中註冊,冒號後面的所有程式碼都是marker說明,包括換行 ```ini [pytest] markers = slow: marks tests as slow (deselect with '-m "not slow"') serial ``` 更高階的,可以在pytest_configure hook函式中註冊,這主要用在第三方外掛 ```python def pytest_configure(config): config.addinivalue_line( "markers", "env(name): mark test to run only on named environment" ) ``` # 簡要回顧 本文介紹了5個pytest內建的marker,接著介紹瞭如何自定義marker和註冊marker。通過marker,可以讓我們更靈活的執行用例。 *參考資料* docs-pytest-org-e