1.簡介
Pytest是基于python語言的單元測試框架,也是一個命令行工具,具有以下特點:
- 入門簡單,易上手
- 支持大量的第三方插件,如:失敗重試,控制用例執行順序等
- 基于配置文件可以簡單的集成CI(持續集成)工具中
2.快速入門
安裝
pip install pytest

基本格式
def add(x, y): return x+y class TestAddFunc(object): # 測試用例的類名必須以 Test 為開頭 def test_01(self): # 方法名和函數名必須以 test 為開頭 print(add(10,20)) def test_02(self): print(add(10, 20)) def test_03(self): print(add(10, 20))
測試運行
pytest中提供了三種方式給測試 人員運行測試用例
- 命令行運行
pytest -s -v 文件名 # -s:輸出測試用例中的print打印的信息 # -v: 輸出測試用例的類名和方法名 # -x: 一旦發現測試用例失敗,立即停止運行
# no:warnings: 不顯示警告
- Pycharm運行
運行測試測試文件即可

- mian函數運行
# pytest.main(["模塊文件名::類名::方法名字","參數"]) pytest.main(["./demo/pytest_01_基本格式","-sv"]) # pytest.main(["./demo/pytest_01_基本格式::TestAddFunc","-sv"]) # pytest.main(["./demo/pytest_01_基本格式::TestAddFunc::test_01","-sv"])

測試腳手架
- 方法級別 :setup 和 teardown ----> 每個用例都會執行
- 類級別: setup_class 和 teardown_class ----> 每個測試類都會執行
- 模塊級別: setup_module 和 teardown_module ----> 每個測試模塊都會執行
import pytest def add(x, y): return x + y def setup_module(): print("模塊初始化") def teardown_module(): print("模塊結束") class TestAddFunc(object): # 測試用例的類名必須以 Test 為開頭 def setup_class(self): print("類初始化") def teardown_class(self): print("類結束") def setup(self): print("用例初始化") def teardown(self): print("用例結束") def test_01(self): # 方法名和函數名必須以 test 為開頭 print(add(10, 20)) def test_02(self): print(add(10, 20)) def test_03(self): print(add(10, 20)) if __name__ == '__main__': # pytest.main(["模塊文件名::類名::方法名字","參數"]) pytest.main(["./demo/pytest_01_基本格式", "-sv"]) # pytest.main(["./demo/pytest_01_基本格式::TestAddFunc","-sv"]) # pytest.main(["./demo/pytest_01_基本格式::TestAddFunc::test_01","-sv"])
以上的執行順序:

基于配置文件運行pytest
在pytest提供的終端運行測試用例的方法上,pytest也支持使用配置文件來指定運行的參數,常用的配置文件名為
- pytest.ini
- tox.ini
- setup.cfg
配置文件一般保存在項目的根目錄下
pytest.ini
[pytest] # 指定運行參數 addopts = -s -v # 搜索測試文件的目錄路徑 testpaths = ./ # 搜索測試文件名格式 python_files = test_*.py # 搜索測試類格式 python_classes = Test* # 搜索測試方法名格式 python_functions = test_*
運行用例
pytest

斷言
pytest中的斷言就是python中的關鍵字assert
格式
assert 表達式, 斷言錯誤提示信息
import pytest def add(x, y): return x+y class TestAddFunc(object): # 測試用例的類名必須以 Test 為開頭 def test_01(self): # 方法名和函數名必須以 test 為開頭 res = add(10,20) assert res == 30,"兩者不相等" def test_02(self): res = add(10, 20) assert type(res) is str, "數據類型不是字符串"

跳過
根據特定的條件,不執行標識的測試函數
@pytest.mark.skipif(判斷條件, reson="跳過原因")
import pytest version = (2,1,2) def add(x, y): return x+y class TestAddFunc(object): def test_01(self): res = add(10,20) assert res == 30,"兩者不相等" @pytest.mark.skipif(version>(2,1,1),reason="版本太低,不測試") def test_02(self): res = add(10, 20) assert type(res) is str, "數據類型不是字符串"

提示:該跳過裝飾器可以添加在函數上也可以添加在類上面
參數化
參數化可以將多個測試用例簡化為一個或者幾個測試函數,說白了就是多個測試用例具有相同的測試參數
import pytest version = (2,1,2) def add(x, y): return x+y class TestAddFunc(object): @pytest.mark.parametrize("args",[{'x':10, "y":20,}, {'x':'10', "y":'20',}, {'x':20, "y":20,}]) def test_start(self, args): res = add(**args) print(res) assert res == 30,"兩者不相等"

3.進階使用
fixture
fixture有個scope的參數,可以控制fixture的作用范圍(從大到小):session>mudule>class>function
session:多個文件調用一次,可以跨.py文件調用 mudule: 每一個.py調用一次 class: 每個類調用一次 function:每一個方法或者函數調用一次
- 實現參數化效果
簡單使用
import pytest @pytest.fixture(scope="class") def fixture_data(): print("運行fixture") return 10, 20 def add(x, y): return x+y class TestAddFunc(object): def test_01(self, fixture_data): # 參數名字就是腳手架的名字,必須保持一致 res = add(fixture_data[0],fixture_data[1]) assert res == 30,"兩者不相等" def test_02(self, fixture_data): res = add(fixture_data[0],fixture_data[1]) assert res == 30,"兩者不相等"

多個腳手架使用
import pytest @pytest.fixture(scope="class") def fixture_data_01(): print("運行fixture-01") return 10, 20 @pytest.fixture(scope="class") def fixture_data_02(): print("運行fixture-02") return 10, 30 def add(x, y): return x+y # 可以顯示的指明調用的fixture,不加也是可以的,主要是便于閱讀代碼 @pytest.mark.usefixtures("fixture_data_01") @pytest.mark.usefixtures("fixture_data_02") class TestAddFunc(object): def test_01(self, fixture_data_01): res = add(fixture_data_01[0],fixture_data_01[1]) assert res == 30,"兩者不相等" def test_02(self, fixture_data_02): res = add(fixture_data_02[0],fixture_data_02[1]) assert res == 40,"兩者不相等"
- 自動執行
可以實現類似setup和teardown的作用,在執行用例之前或者之后運行
import pytest
# 在每一個測試用例執行之前執行 @pytest.fixture(scope="function", autouse=True) def fixture_data_01(): print("運行fixture-01") return 10, 20 # 不會自動執行 @pytest.fixture(scope="function") def fixture_data_02(): print("運行fixture-02") return 10, 30 def add(x, y): return x+y class TestAddFunc(object): def test_01(self): pass def test_02(self): pass
- yied使用
在上面的案例中我們可以實現類似setup的作用,即可以在用例執行之前運行,那么如何實現teardown的作用呢?就可以使用yield的關鍵字
說的更直白一點就是yied關鍵字可以實現teardown和setup的集合,同時yied的返回值可以充當用例的執行入參,非常類似python中的上下文操作
import pytest @pytest.fixture(scope="function", autouse=True) def fixture_data_01(): print("開始運行fixture-01") yield 10 print("結束運行fixture-01") def add(x, y): return x+y class TestAddFunc(object): def test_01(self, fixture_data_01): print(fixture_data_01) pass def test_02(self): pass
- fixture代碼分離
我們可以將fixture的代碼寫好,放在一個名為conftest.py文件中,可以被pytest自動識別,進而實現了測試用例和fixture的分離
conftest.py應該和測試代碼放在同一個目錄下,在實際開發中可以有多個conftest.py文件,每個文件的作用域是當前自身所在的當前目錄及其子目錄
conftest.py 代碼
import pytest @pytest.fixture(scope="function", autouse=True) def fixture_data_01(): print("開始運行fixture-01") yield 10 print("結束運行fixture-01")測試用例代碼
def add(x, y): return x+y class TestAddFunc(object): def test_01(self, fixture_data_01): print(fixture_data_01) pass def test_02(self): pass
第三方組件的使用
- 控制用例執行順序
pytest默認執行用例的順序是按照源代碼的上下順序執行的,如果希望控制執行順序,可以通過第三方組件pytest-ordering實現
安裝
pip install pytest-ordering
使用
執行順序為:優先執行正序排序的方法,在執行沒有排序的方法,最后執行負數排序的方法,如果多個方法都是正序,則先執行排序小,負數亦然import pytest class TestAdd(object): @pytest.mark.run(order=-1) def test_01(self): print("test_01") @pytest.mark.run(order=-2) def test_02(self): print("test_02") def test_03(self): print("test_03") @pytest.mark.run(order=1) def test_04(self): print("test_04") @pytest.mark.run(order=2) def test_05(self): print("test_05")

注意:pytest-ordering組件不能和fixture一起使用,會報錯的,如果在使用pytest_ordering的情況下還需要參數化,可以使用@pytest.mark.parameterze - 失敗用例重試
針對網絡場景和服務端性能不穩定的情況下,進行測試用常常發生用例運行失敗的情況,我們可以指定重試次數,已達到重試更加準確的結果
安裝
pip install pytest-rerunfailures
使用在執行pytest中添加執行參數即可 --reruns n :重試次數 --reruns-delay m:(重試間隔)
全局失敗用例重試
配置文件pytest.ini[pytest] # 指定運行參數 addopts = -s -v -p no:warnings --reruns 3 --reruns-delay 2 # 搜索測試文件的目錄路徑 testpaths = ./ # 搜索測試文件名格式 python_files = pytest_*.py # 搜索測試類格式 python_classes = Test* # 搜索測試方法名格式 python_functions = test_*
不通過的用例會最多執行3次,每次重試間隔2秒,最后返回結果
局部失敗用例測試import pytest def add(x, y): return x+y class TestAddFunc(object): def test_01(self, fixture_data_01): print(fixture_data_01) pass @pytest.mark.flaky(reruns=4, reruns_delay=2) def test_02(self): assert 1== 2

注意:
局部參數會覆蓋全局參數,也就是說使用了局部參數,全局用法就會失效
與pytest.fixture腳手架也會存在沖突,不能混合使用 - 用例并發運行
當測試用例非常多的時候,一條條執行是很浪費時間的。如果測試用例之間彼此獨立,沒有任何依賴關系,就可以并發運行用例,從而節省時間
pytest-xdist可以實現并發運行,屬于進程級別的
安裝
pip install pytest-xdist
使用
在運行測試用例的時候添加一個參數即可,可以指定運行測試的時候所開啟的進程數量-n 4 :使用4個進程運行, -n auto : 自動檢測CPU核數,根據CPU核數創建對應數量的進程個數
pytest.ini代碼[pytest] # 指定運行參數 addopts = -s -v -p no:warnings --reruns 3 --reruns-delay 2 -n 4 # 搜索測試文件的目錄路徑 testpaths = ./ # 搜索測試文件名格式 python_files = pytest_*.py # 搜索測試類格式 python_classes = Test* # 搜索測試方法名格式 python_functions = test_*
測試用例
import pytest class TestAdd(object): @pytest.mark.run(order=-1) def test_01(self): print("test_01") @pytest.mark.run(order=-2) def test_02(self): print("test_02") assert 1 ==1 def test_03(self): print("test_03") @pytest.mark.run(order=1) def test_04(self): print("test_04") @pytest.mark.run(order=2) def test_05(self): print("test_05")
運行結果

- 生成HTML格式測試用例
生成的測試報告很簡單,在實際的開發中,還是使用Allure來生成測試報告
安裝
pip install pytest-html
使用
在執行測試用例的時候,添加參數 --html=生成測試報告路徑 即可
pytest.ini文件[pytest] # 指定運行參數 addopts = -s -v -p no:warnings --reruns 3 --reruns-delay 2 -n 4 --html=report.html # 搜索測試文件的目錄路徑 testpaths = ./ # 搜索測試文件名格式 python_files = pytest_*.py # 搜索測試類格式 python_classes = Test* # 搜索測試方法名格式 python_functions = test_*

浙公網安備 33010602011771號