Pytest自動化測試 - 簡易教程
簡介
pytest是動態程式語言Python專用的測試框架,它具有易於上手、功能強大、可擴充套件性好、相容性強、效率高、第三方外掛豐富等特點。
功能特徵:
- 完整的文件,包括安裝,教程和PDF文件
- 簡單而又詳細的斷言模式(使用純assert語句)
- 自動發現測試模組和功能(以test為標識)
- 可以執行unittest和nose框架的測試用例
- 靈活的韌體,用於管理小型或引數化的長期測試資源
- 豐富的外掛架構,擁有三百多個外部外掛和豐富的社群
編寫規則:
- 測試檔案以test_開頭(以_test結尾也可以)
- 測試類以Test開頭,並且不能帶有 init 方法
- 測試函式以test_開頭
- 斷言使用基本的assert即可
自動發現規則:
如果未指定任何引數,則從testpaths(如果已配置)或當前目錄開始收集。
另外,命令列引數可以在目錄、檔名或節點ID的任何組合中使用。
在這些目錄中,搜尋包含 test_*.py 或 *_test.py 的測試檔案。
從這些檔案中,收集以test字首的測試方法,或者在Test字首的測試類(無__init__方法)中的以test字首的測試方法。
官方文件:https://docs.pytest.org/en/latest/contents.html
安裝
開啟bash命令列,執行以下命令:
pip install -U pytest
檢查是否安裝了正確的版本:
$ pytest --version pytest 6.1.2
示例
建立一個簡單的測試函式:
# test_sample.py # 被測功能 def func(x): return x + 1 # 測試成功 def test_pass(): assert func(3) == 4 # 測試失敗 def test_fail(): assert func(3) == 5
現在開始執行測試功能:
E:\workspace-py\Pytest>pytest ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 2 items test_sample.py .F [100%] =============================================================================== FAILURES ================================================================================ _______________________________________________________________________________ test_fail _______________________________________________________________________________ def test_fail(): > assert func(3) == 5 E assert 4 == 5 E + where 4 = func(3) test_sample.py:16: AssertionError ======================================================================== short test summary info ======================================================================== FAILED test_sample.py::test_fail - assert 4 == 5 ====================================================================== 1 failed, 1 passed in 0.16s ======================================================================
這裡未指定測試用例,pytest將依據自動發現規則檢索並執行測試,等同於 pytest ./test_sample.py
- pytest 使用 . 標識測試成功(PASSED)
- pytest 使用 F 標識測試失敗(FAILED)
- 可以使用 -v 選項,顯示測試的詳細資訊
- 可以使用 -h 檢視 pytest 的所有選項
標記
預設情況下,pytest 會遞迴查詢當前目錄下所有以 test 開始或結尾的 Python 指令碼,並執行檔案內的所有以 test 開始或結束的函式和方法。
1、如果你想指定執行測試用例,可以通過 ::
顯式標記(檔名::
類名::方法名
)。
E:\workspace-py\Pytest>pytest test_sample.py::test_pass ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 1 item test_sample.py . [100%] =========================================================================== 1 passed in 0.05s ===========================================================================
2、如果你想選擇一些測試用例,可以使用 -k
模糊匹配。
E:\workspace-py\Pytest>pytest -k pass test_sample.py ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 2 items / 1 deselected / 1 selected test_sample.py . [100%] ==================================================================== 1 passed, 1 deselected in 0.02s ====================================================================
3、如果你想跳過個別測試用例,可以使用 pytest.mark.skip(),或者 pytest.mark.skipif(條件表示式)。
# 測試失敗 @pytest.mark.skip() def test_fail(): assert func(3) == 5
E:\workspace-py\Pytest>pytest -v test_sample.py ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 -- c:\python37\python.exe cachedir: .pytest_cache metadata: {'Python': '3.7.3', 'Platform': 'Windows-7-6.1.7601-SP1', 'Packages': {'pytest': '6.0.2', 'py': '1.9.0', 'pluggy': '0.13.0'}, 'Plugins': {'allure-pytest': '2.8. 18', 'cov': '2.10.1', 'html': '2.1.1', 'metadata': '1.8.0', 'rerunfailures': '9.1', 'xdist': '2.1.0'}} rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 2 items test_sample.py::test_pass PASSED [ 50%] test_sample.py::test_fail SKIPPED [100%] ===================================================================== 1 passed, 1 skipped in 0.07s ======================================================================
4、如果你想捕捉一些異常,可以使用pytest.raises()。
# test_raises.py def test_raises(): with pytest.raises(TypeError) as e: connect('localhost', '6379') exec_msg = e.value.args[0] assert exec_msg == 'port type must be int'
5、如果你事先知道測試函式會執行失敗,但又不想直接跳過,而是希望顯示的提示,可以使用pytest.mark.xfail()。
# 測試失敗 @pytest.mark.xfail() def test_fail(): assert func(3) == 5
E:\workspace-py\Pytest>pytest -k fail test_sample.py ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 2 items / 1 deselected / 1 selected test_sample.py x [100%] =================================================================== 1 deselected, 1 xfailed in 0.05s ====================================================================
6、如果你想對某個測試點進行多組資料測試,可以使用 pytest.mark.parametrize(argnames, argvalues) 引數化測試,即每組引數都獨立執行一次測試。
注意:以往我們可以把這些引數寫在測試函式內部進行遍歷,但是當某組引數導致斷言失敗,測試則就終止了。
# 測試成功 @pytest.mark.parametrize('data', [1, 2, 3]) def test_pass(data): assert func(data) == 4
E:\workspace-py\Pytest>pytest -k pass test_sample.py ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 4 items / 1 deselected / 3 selected test_sample.py FF. [100%] =============================================================================== FAILURES ================================================================================ _____________________________________________________________________________ test_pass[1] ______________________________________________________________________________ data = 1 @pytest.mark.parametrize('data', [1, 2, 3]) def test_pass(data): > assert func(data) == 4 E assert 2 == 4 E + where 2 = func(1) test_sample.py:11: AssertionError _____________________________________________________________________________ test_pass[2] ______________________________________________________________________________ data = 2 @pytest.mark.parametrize('data', [1, 2, 3]) def test_pass(data): > assert func(data) == 4 E assert 3 == 4 E + where 3 = func(2) test_sample.py:11: AssertionError ======================================================================== short test summary info ======================================================================== FAILED test_sample.py::test_pass[1] - assert 2 == 4 FAILED test_sample.py::test_pass[2] - assert 3 == 4 =============================================================== 2 failed, 1 passed, 1 deselected in 0.17s ===============================================================
韌體
韌體(Fixture)是一些函式,pytest 會在執行測試函式之前(或之後)載入執行它們。
我們可以利用韌體做任何事情,其中最常見的可能就是資料庫的初始連線和最後關閉操作。
1、Pytest使用pytest.fixture()定義韌體,為了在實際工程中可以更大程度上覆用,我們更多的是使用檔案conftest.py集中管理韌體(pytest會自動呼叫)。
# conftest.py import pytest @pytest.fixture() def data(): return 3
# 測試成功 def test_pass(data): assert func(data) == 4
E:\workspace-py\Pytest>pytest -k pass test_sample.py ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 2 items / 1 deselected / 1 selected test_sample.py . [100%] ==================================================================== 1 passed, 1 deselected in 0.05s ====================================================================
2、Pytest 使用 yield 關鍵詞將韌體分為兩部分,yield 之前的程式碼屬於預處理,會在測試前執行;yield 之後的程式碼屬於後處理,將在測試完成後執行。
# conftest.py import pytest @pytest.fixture() def db(): print('opened') yield print('closed')
# 測試成功 def test_pass(db): assert func(3) == 4
E:\workspace-py\Pytest>pytest -s -k pass test_sample.py ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 2 items / 1 deselected / 1 selected test_sample.py opened .closed ==================================================================== 1 passed, 1 deselected in 0.02s ====================================================================
3、為了更精細化控制韌體,pytest使用作用域來進行指定韌體的使用範圍。
在定義韌體時,通過 scope 引數宣告作用域,可選項有:
- function: 函式級,每個測試函式都會執行一次韌體(預設值);
- class: 類級別,每個測試類執行一次,所有方法都可以使用;
- module: 模組級,每個模組執行一次,模組內函式和方法都可使用;
- session: 會話級,一次測試只執行一次,所有被找到的函式和方法都可用。
# conftest.py import pytest @pytest.fixture(scope='function', autouse=True) def func_scope(): pass @pytest.fixture(scope='module', autouse=True) def mod_scope(): pass @pytest.fixture(scope='session') def sess_scope(): pass @pytest.fixture(scope='class') def class_scope(): pass
# 測試成功
@pytest.mark.usefixtures('sess_scope')
def test_pass(class_scope):
assert func(3) == 4
E:\workspace-py\Pytest>pytest --setup-show -k pass test_sample.py ========================================================================== test session starts ========================================================================== platform win32 -- Python 3.7.3, pytest-6.0.2, py-1.9.0, pluggy-0.13.0 rootdir: E:\workspace-py\Pytest plugins: allure-pytest-2.8.18, cov-2.10.1, html-2.1.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0 collected 2 items / 1 deselected / 1 selected test_sample.py SETUP S sess_scope SETUP M mod_scope SETUP C class_scope SETUP F func_scope test_sample.py::test_pass (fixtures used: class_scope, func_scope, mod_scope, sess_scope). TEARDOWN F func_scope TEARDOWN C class_scope TEARDOWN M mod_scope TEARDOWN S sess_scope ==================================================================== 1 passed, 1 deselected in 0.02s ====================================================================
我們可以把韌體作為入參,還可以使用@pytest.mark.usefixtures('class_scope'),或者指定autouse引數讓韌體自動執行。
並且還可以指定params引數來實現韌體的引數化,以及指定name引數來修改韌體名。
- 可以使用 -s 選項,顯示print列印資訊
- 可以使用 --setuo-show 選項,顯示詳細的韌體資訊
4、內建韌體:
tmpdir & tmpdir_factory:用於臨時檔案和目錄管理,預設會在測試結束時刪除。
pytestconfig:用於讀取命令列引數和配置檔案
capsys:用於捕獲 stdout 和 stderr 的內容,並臨時關閉系統輸出。
monkeypath:用於執行時動態修改類或模組。
recwarn:用於捕獲程式中 warnings 產生的警告。
Pytest學習手冊:https://learning-pytest.readthedocs.io/zh/latest/index.html
作者:Leozhanggg
出處:https://www.cnblogs.com/leozhanggg/p/14035202.html
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。
&n