Pytest是一種基於python的測試框架,用於編寫和運行測試代碼。pytest主要用來測試API,但也可以進行一些復雜的測試,像測試數據庫或UI等。
Pytest 的優勢:
- Pytest 可以並行執行多個tests, 減少 test-suite 的執行時間 [第八章]
- Pytest 可以自動地檢測測試文件或測試函數,不需要被顯示地提及 [第二章]
- test_*.py 文件
- *_test.py 文件
- test* 函數
- Pytest 允許我們在運行過程中跳過一些test,執行整個test-suite的子集 [第三,四,五,六章]
- Pytest 免費開源,易學
(一)pytest 安裝
安裝 pytest: pip3 install pytest
查看 pytest 是否安裝成功:pip3 show pytest
若安裝成功,將會有如下顯示
Name: pytest
Version: 5.3.5
Summary: pytest: simple powerful testing with Python
Home-page: https://docs.pytest.org/en/latest/
Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
Author-email: None
License: MIT license
Location: /usr/local/lib/python3.7/site-packages
Requires: pluggy, py, importlib-metadata, more-itertools, wcwidth, attrs, packaging
Required-by:
(二)Pytest 測試文件/函數
2.1. Pytest 測試文件:Pytest命令會自動運行當前目錄下所有' test_*.py' 和 '_test.py' 文件 [1], 自動默認這些文件是測試文件。當然,我們也可以顯示地指示要要運行的測試文件或測試目錄。
Running pytest without mentioning a filename will run all files of format test_*.py or *_test.py in the current directory and subdirectories. Pytest automatically identifies those files as test files. We can make pytest run other filenames by explicitly mentioning them.
pytest a.py
pytest dirname
2.2. Pytest 測試函數:Pytest需要被測試函數以‘test’開頭命名,不以'test' 開頭的函數會被pytest自動忽略,pytest不可以像指定文件一樣顯示地指定被測函數,即測試函數必須以'test'開頭。
Pytest requires the test function names to start with test. Function names which are not of format test* are not considered as test functions by pytest. We cannot explicitly make pytest consider any function not starting with test as a test function.
【例子1】創建Pytest文件夾,新建文件 test_nn.py 和 test_greater.py 和 test.py. 目錄結構如下
Pytest
|- test.py
|- test_great.py: test_greater(); test_greater_equal(); test_less();
|- test_nn.py: testx(); test_great_haha();
運行 pytest 命令,只顯示 test_great.py 和 test_nn.py 文件,因為[1],其中 test_great.py 代碼如下
def test_greater(): num = 100 assert num > 100 def test_greater_equal(): num = 100 assert num >= 100 def test_less(): num = 100 assert num < 200
運行pytest 和 pytest -v 命令,運行結果分別如下左右圖所示,-v 顯示更

(三)Pytest 可以選擇性測試完整test-suite的某個子集(subset)
3.1. 依照文件名匹配的方式選擇測試子集 (select tests to run based on substring matching of test names)
pytest -k <substring> -v
針對【例子1】
pytest -k great -v 命令 ('great' 對應 <substring>項) 執行所有帶有great的測試,

注意:pytest -k 'substring' -v: 可識別當前目錄下所有匹配文件中帶有 ‘substring’ 的測試函數。
3.2. 根據 標記 選擇測試組進行測試 (select tests groups to run based on the markers applied)
添加相關包:import pytest
插入標記:@pytest.mark.<markname> 運行帶標記的測試:pytest -m <markname> -v
[例子2] 帶標記 (marker) 的 test_mark.py 文件如下
import pytest @pytest.mark.great def test_greater(): num = 100 assert num > 100 @pytest.mark.great def test_greater_equal(): num = 100 assert num >= 100 @pytest.mark.others def test_less(): num = 100 assert num < 200

(四)Pytest 通過 Fixture 和 conftest.py 加載測試所需調用
@pytest.fixture
Fixture 是測試的輸入,測試被執行前, 涉及到的 fixture 會被執行到。Pytest 提供了 @pytest.fixture 將該部分代碼自動附加到測試函數中
【注意】(1) 如果不加 @pytest.fixture 的話,這部分被測試函數調用的代碼不會加載進入測試函數,pytest運行時會報錯 ...
(2) @pytest.fixture 的作用域是當前文件,在其他文件中調用該 fixture 對應的函數會報錯.We cannot use that fixture in another test file
Fixtures are functions, which will run before each test function to which it is applied. Fixtures are used to feed some data to the tests such as database connections, URLs to test and some sort of input data. Therefore, instead of runnng the same code for every test, we can attach fixture to the tests and it will run and return the data to the test before executing each test.
【例子3.1】創建 test_div.py 內容如下
import pytest @pytest.mark.great def test_greater(): num = 100 assert num > 100 @pytest.mark.great def test_greater_equal(): num = 100 assert num >= 100 @pytest.mark.others def test_less(): num = 100 assert num < 200

為了克服(2)的限制, 在多個測試文件中調用 Fixture, 我們需要 conftest.py 統一定義存儲 Fixture
【例子3.2】新建 conftest.py 內容如下
import pytest @pytest.fixture def input_value(): input = 39 return input
# content of file 'test_14' def test_divisible_14(input_value): assert input_value%14 ==0 # content of file 'test_15' def test_divisible_15(input_value): assert input_value%15 ==0 # content of file 'test_16' def test_divisible_16(input_value): assert input_value%16 ==0
運行 pytest -k divisible -v 時,由於conftest.py 文件已聲明 fixture, 所以input_value() 函數會自動加載到每次調用之前,測試正常進行
(五)Pytest指定測試,驗證實際輸出和期望輸出是否一致
Pytest 用 @pytest.mark.parametrize() 測試指定的<input, output>對
【例子4】添加@pytest.mark.parametrize()子句如下
import pytest @pytest.mark.parametrize("num,output",[(1,11),(2,22),(3,35),(4,44)]) def test_multiplication_11(num,output): assert 11*num == output

(六)Pytest 跳過某些測試/屏蔽某些測試錯
@pytest.mark.skip 用來掉過某些測試
@pytest.mark.xfail 用來屏蔽某些測試的錯誤
【例子5】新建 test_op.py 文件,內容如下
import pytest @pytest.mark.xfail @pytest.mark.great def test_greater(): num = 100 assert num > 100 @pytest.mark.great def test_greater_equal(): num = 100 assert num >= 200 @pytest.mark.skip @pytest.mark.others def test_divisible(input_value): assert input_value < 200
運行 pytest test_op.py 運行結果如下,test_greater() 錯誤被屏蔽,test_greater_equal()正常報錯,test_divisible()被測試跳過
(七)Pytest測試停止
cmd: pytest --maxfail = <num>
當檢測出的錯誤個數等於num時,pytest 測試結束,不在對后面的代碼進行測試
(八)Pytest 並行測試
pip install pytest-xdist // 安裝pytest 並行測試工具
pytest -n <num> // runs in <num> workers
(九)Pytest 將測試結果記錄到 xml 文件中
pytest -v --junitxml=<filename>
【例子6】 pytest -v --junitxml="result.xml"
參考(Reference)
