一、Pytest介紹
官網:https://docs.pytest.org/en/7.1.x/
——Pytest是一個測試用例的管理框架,在Unitest基礎上做的一個全面的升級.
- 集成度更高,而且更加靈活的一個測試框架(測試用例開頭不想使用test打頭,可進行自定義)
- 運行的順序是自上而下,不是像Unitest是根據字母來進行執行
- 全程都是基於指令的形式來運行(通過指令就可以直接生成測試報告)
- 可以直接使用Unitest
- Pytest默認規則是:尋找當前路徑下所有的文件與子文件中以test開頭或是結尾的文件夾、文件、函數作為識別對象,所以我們在創建Python模塊時以及命名函數時,都需要是【test開頭或是結尾】,不然則不會被識別到
- Pytest默認不輸出任何打印信息,如果要看打印信息,需要在運行時添加 “ -s ” 的指令
- 多條指令一同運行時,需要通過 空格 進行區分,在 main 函數中,是通過 逗號 來分割
- -v 用於詳細顯示日志信息是否通過
- -rA 測試結果的簡單統計
-
-q 只顯示運行結果以及時間
- 預置函數: 一般可以通過一個配置文件直接進行管理,配置文件命名一定要是 conftest.py(不能是其他的),用於前期的數據准備,如:可把獲取token放入到預置函數中
- fixture是pytest中的一大利器
二、Pytest環境部署
pip install pytest
三、操作使用
import pytest def test_01(): print('test-1') def test_02(): print('test-2') if __name__ == "__main__": pytest.main() #不會打印出信息 pytest.main(['-s']) #可以直接打印出信息
結果:

我們可以從結果中看出,並沒有打印出信息,如果相關查看打印信息,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s運行

- 多個test文件、函數,指定自定義運行
(1)運行指定的 .py模塊,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s test_case1.py

結果:

(2)運行 .py模塊下某個函數,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s test_case1.py::test_002

結果:

方法二:代碼中指定:pytest.main(['-s','test_case1.py::test_002'])

(3)指定 .py模塊下例運行哪幾個,以及指定運行順序,可在【輸出結果欄】中的“Terminal"中,進入此路徑下,輸入命令:pytest -s test_case1.py::test_002 test_case1.py::test_001

(4)"-V " 打印出用例是否通過結果
import pytest def test_01(): print('test-1') def test_02(): print('test-2') if __name__ == "__main__": pytest.main(['-v']) # "-V"會打印出用例是否通過結果
結果:

(5)mian中命令,可放在一起寫,如:
import pytest def test_01(): print('test-1') def test_02(): print('test-2') if __name__ == "__main__": pytest.main(['-sv'])
結果:

(6) -rA 測試結果的簡單統計
import pytest def test_01(): print('test-1') def test_02(): print('test-2') if __name__ == "__main__": pytest.main(['-rA'])
結果:

(7)-q 只顯示運行結果以及時間
import pytest def test_01(): print('test-1') def test_02(): print('test-2') if __name__ == "__main__": pytest.main(['-q'])
結果:

四、配置文件 conftest.py 中 fixture 的使用:
(1)基本使用
-
conftest.py
import pytest #預置函數 @pytest.fixture() #裝飾器 def keep(): print('堅持下去!!!')
-
在test_case.py中執行代碼
import pytest def test_01(keep): print('test-1') def test_02(): print('test-2') if __name__ == "__main__": pytest.main(['-s'])
結果:

我們可以看出,是相當於把 conftest.py 中的預置函數keep傳給了 test_case.py中的 test_01函數,執行代碼時,首先運行的conftest.py 中的預置函數keep,然后在依次執行的test_case.py中的2個函數。
(2)斷言操作
-
conftest.py
import pytest @pytest.fixture() def keep01(): return 1
-
在test_case.py中執行代碼
import pytest def test_01(keep01): assert keep01 == 1,'失敗' assert keep01 == 2, '失敗' if __name__ == "__main__": pytest.main(['-s'])
結果:

(3)fixture中scope參數定義的4種等級(默認等級是funtion):
- session:在本次session級別中只執行一次(說的是運行結果中標題顯示的:==test session starts==)
- module: 在模塊級別中只執行一次,就是說在第一個模塊運行一次就可以了,之后的模塊在使用,直接取就可以了
- class:在類級別中執行一次
- funtion:在函數級別中執行,每有一個函數就執行一次
-
conftest.py
import pytest @pytest.fixture(scope='session') #字符串中還可以是:module、class、funtion def keep01(): return 1
五、前置、后置條件
-
setup_function、teardown_function
import pytest #前置、后置條件 def setup_function(): print('===setup_function===') def teardown_function(): print('***teardown_function***') def test_01(keep01): print('test_01') if __name__ == "__main__": pytest.main(['-s'])
結果:

-
setup_module、teardown_module
import pytest #前置、后置條件,函數名字是固定寫法,不可以自定義 def setup_function(): print('===setup_function===') def teardown_function(): print('***teardown_function***') def setup_module(): print('。。。setup_module。。。') def teardown_module(): print('+++teardown_module+++') def test_01(keep01): print('test_01') if __name__ == "__main__": pytest.main(['-s'])
結果:

六、類的使用
import pytest #前置、后置條件 def setup_function(): print('===setup_function===') def teardown_function(): print('***teardown_function***') def setup_module(): print('。。。setup_module。。。') def teardown_module(): print('+++teardown_module+++') #pytest中class對象的定義:建議以test開頭,不需要繼承了 class TestDemo: def test_d1(self): print('類===test_d1===') def test_d2(self): print('類***test_d2***') def test_01(keep01): print('普通函數test_01') if __name__ == "__main__": pytest.main(['-s'])
結果:

* 我們從結果可以看出setup_function晚於類執行,在類里面是沒有執行setup_function的。也就是說在類外邊定義的setup_function,只能在類外邊起作用,在類里面不起作用。
-
在類里面使用setup、teardown
import pytest class TestDemo: def setup(self): print('class setup') def teardown(self): print('class teardown') def test_d1(self): print('類===test_d1===') def test_d2(self): print('類***test_d2***') if __name__ == "__main__": pytest.main(['-s'])
結果:

-
在類里面使用setup、teardown、setup_class、teardown_class、setup_metchod、teardown_metchod
import pytest #前置、后置條件 class TestDemo: def test_001(self): print('類:test_001') def setup(self): print('類:setup') def teardown(self): print('類:teardown') def setup_class(self): print('類===setup_class===') def teardown_class(self): print('類***teardown_class***') def setup_method(self): print('類===setup_method===') def teardown_method(self): print('類***teardown_method***') if __name__ == "__main__": pytest.main(['-s'])
結果:

- 在class中 前置、后置 函數的運行順序:
- setup class
- setup method
- setup
- teardown
- teardown methon
- teardown class
七、pytest中用例的管理手段:mark
——可以通俗的理解為:是一個批量管理工具,標記好了,就可以批量的運行標記為一樣的用例了。
- 可以通過mark裝飾器對所有的用例進行標記,不同的標記區分進行管理
- 在一個py文件還是多個py文件都會生效
(1)運行一種被標記的用例
import pytest @pytest.mark.webui def test_01(): print('web01') @pytest.mark.webui def test_02(): print('web02') @pytest.mark.app def test_03(): print('app01') @pytest.mark.app def test_04(): print('app02') if __name__ == "__main__": pytest.main(['-s','test_case2.py','-m webui']) # -m webui:指定運行是webui的用例
結果:

(2)用例被@pytest.mark標記多個時,運行其中一種被標記的用例
import pytest @pytest.mark.webui def test_01(): print('web01') @pytest.mark.webui def test_02(): print('web02') @pytest.mark.app @pytest.mark.temp def test_03(): print('app01') @pytest.mark.app @pytest.mark.temp def test_04(): print('app02') if __name__ == "__main__": pytest.main(['-s','test_case2.py','-m temp']) # -m webui:指定運行是webui的用例
結果:

(2)同時運行多個不同的被標記種類,-m name1,neme2
pytest.main(['-s','test_case2.py','-m app,webui'])
八、運行方式:
1、命令行模式
通過標記表達式執行 pytest -m demo 這條命令會執行被裝飾器@pytest.mark.demo裝飾的所有測試用例 生成html報告: pytest -m demo --html=Report/report.html 生成xml報告: pytest -m demo --junitxml=Report/report.xml 運行指定模塊: pytest -m demo --html=Report/report.html TestCases/test_pytest.py 運行指定測試目錄 pytest -m demo --html=Report/report.html TestCases/ 通過節點id來運行: pytest TestCases/test_pytest.py::TestDemo::test_demo01 通過關鍵字表達式過濾執行 pytest -k "MyClass and not method" 這條命令會匹配文件名、類名、方法名匹配表達式的用例 獲取用例執行性能數據 獲取最慢的10個用例的執行耗時 pytest --durations=10
2、新建run.py文件運行,代碼如下:
pytest.main(["-m","demo","--html=Report/report.html"])
九、pytest框架下的核心配置文件 pytest.ini
——配置在工程的根目錄下,可以全局生效
import pytest @pytest.mark.webui def test_01(): print('web01') @pytest.mark.webui def test_02(): print('web02') @pytest.mark.interface def test_03(): print('interface01') @pytest.mark.interface def test_04(): print('interface02') if __name__ == "__main__": pytest.main(['-s','test_case2.py'])
-
pytest.ini
——ini 文件的名稱必須是pytest.ini,代碼中所標黃名稱必須這樣定義,不可為其他的,否則不起作用
[pytest] #標簽 markers = webui: automation for web ui #冒號后邊是標簽的注釋 interface: automation for interface temp: just for fun #定義到pytest中,讀取哪一些py文件,作為pytest的識別對象 python_files = cema*.py test*.py #配置后,會根據指定的 cema*,運行代碼時,只會執行cema打頭的py文件 #添加多個,逗號隔開,可繼續添加 #定義到pytest中,讀取哪一些類,作為pytest的識別對象 python_classes = Cema* #配置后,類名為開頭Cema的類,就可以正常被識別到執行了 #指定運行的路徑,可以為絕對路徑和相對路徑 testpaths = cema_case2.py ./ #指定運行的函數名 python_functions = vip* test* #單獨配置后,只會運行vip開頭的函數;空格間隔,可配置多個 #打印信息顯示Passed通過/Failed不通過 log_cli = True #log_cli 和 -v的區別: #log_cli會把測試用例的執行顯示的更加完善,會顯示哪個py文件下哪個類哪個函數,如:test_case.py::TestDemo::test_001 類:test_001 PASSED # -v 會把【test session starts】打印信息中的信息更加的全面 #配置命令 addopts = -s -v #配置后,工程py文件下都可使用,pytest.main()中可不用在寫命令
十、斷言機制
- conftest.py
import pytest @pytest.fixture(scope='session') #字符串中還可以是:module、class、funtion def keep(): # 預先配置下 return 1
- pytest.ini
[pytest] markers = webui: automation for web ui interface: automation for interface temp: just for fun python_files = cema*.py #python_classes = Test* estpaths = cema_case2.py python_functions = test* log_cli = True addopts = -s
- cema_case.py——【返回通過】的用例
import pytest @pytest.mark.webui def test_01(keep): assert keep == 1,"成功" if __name__ == "__main__": pytest.main()
結果:

- cema_case.py——【返回不通過】的用例
import pytest @pytest.mark.webui def test_01(keep): assert keep == 2,"失敗" if __name__ == "__main__": pytest.main()
結果:

十一、生成測試報告
——pytest-html 測試報告模塊,cmd中安裝
pip install pytest-html
安裝后,我們配置完了代碼運行結束后,想要生成報告,在Terminal(終端)中輸入:
pytest #輸入pytest 后,點擊Enter(回車鍵) pytest --html=./report/repott.html # =后邊是指定路徑、文件名稱 pytest --html=./report1/repott1.html --self-contained-html #直接生成html文件,不會帶有css樣式文件,發送給其他人時操作簡單
#可放在pytest.ini中的 addopts中
在指定的目錄下會生成:repott.html


測試報告:

十二、常用斷言
- assert xx :判斷 xx 為真
- assert not xx :判斷 xx 不為真
- assert a in b :判斷 b 包含 a
- assert a == b :判斷 a 等於 b
- assert a != b :判斷 a 不等於 b
十三、assert小栗子
想在拋出異常之后輸出一些提示信息,執行之后就方便查看是什么原因了
import pytest # 異常信息 def f(): return 3 def test_function(): a = f() assert a % 2 == 0, "判斷 a 為偶數,當前 a 的值為:%s" % a if __name__ == '__main__': pytest.main(['-s','demo.py'])
結果:

十四、pytest的mark擴展使用
-
@pytest.mark.xfail(reason="")標記為失敗,期望值為失敗,在函數之上使用,pytest.xfail()預期結果失敗,下面的代碼不會執行,在函數里使用
-
@pytest.mark.skip()無條件跳過測試用例,pytest.mark.skipif()有條件跳過測試用力
-
@pytest.mark.parametrize(param1,[1,2,3])多組參數單個用例的執行
-
@pytest.mark.run需要插件pytest-ordering # 控制函數執行順序,@pytest.mark.run(order=1)
-
@pytest.mark.flaky
最多失敗重跑5次,如果失敗延遲2秒重跑,可以結合mark標記使用,@pytest.mark.flaky(reruns=5,reruns-delay=2)
如果是批量執行,命令為:pytest --reruns 5 --reruns-delay2
十五、pytest常用插件擴展
-
pytest-cov代碼覆蓋率的檢測 pytest --cov=src --cov-report = html
-
pytest -sugar 改變pytest默認外觀,增加進度條功能
-
pytest-xdist 並行運行,pytst -n 2
-
pytest-rerunfailures 失敗用例重跑
-
pytest-ordering 執行順序
-
pytest-picked 僅測試上次提交以來已更改的代碼
十六、測試用例的執行
目錄:

1、主函數模式
(1)運行所有 pytest.main()
(2)指定模塊 pytest.main(’[-vs],’,’./testcase/test_day1.py’)
(3)指定目錄pytest.main(’[-vs]’),’./testcase’)
(4)通過nodeid指定用例運行:nodeid由模塊名,分隔符,類名,方法名,函數名組成
#pytest.main(["-vs"],’./interface_testcase/test_day3.py::test_demo11’)
#pytest.main(["-vs"],’./interface_testcase/test_day3.py::TestLogin::test_01_qianghong1’)
2、命令行模式
(1)運行所有:pytest(2)指定模塊 pytest -vs ./testcase/test_day1.py
(3)指定目錄 pytest -vs ./testcase
(4)通過nodeid指定用例運行:nodeid由模塊名,分隔符,類名,方法名,函數名組成
pytest -vs ./interface_testcase/test_day3.py::test_demo11
pytest -vs ./interface_testcase/test_day3.py::TestLogin::test_01_qianghong1
參數詳解: -s:表示輸出調試信息,包括print打印的信息 -v顯示更詳細的信息 -vs一起使用 -n支持多線程或者分布式運行測試用例 #如 #pytest.main([’-vs’,’./testcase/test_day1.py’,’-n=2’]) # pytest -vs ./testcase/test_day1.py -n 2 #reruns==number 表示失敗用例重跑 #pytest -vs ./testcase/test_day2.py --reruns 2 #pytest.main([’–vs’,’./testcase/test_day2.py’,‘reruns=2’]) #失敗得的用例重跑兩次 #-x表示只要一個用例報錯,那么測試停止運行 #–maxfail=2 出現兩個失敗用例停止 #-k 根據測試用例的部分字符串指定測試用例 pytest -vs test_day2 -k “yang”
pytest--命令行常用參數:http://t.zoukankan.com/zouzou-busy-p-11295444.html
