該文章是從我個人的微信公眾號上復制過來的,因此有些圖片不能正常顯示 ,懶得去一個個重新處理圖片了,大家可以點擊微信公眾號的鏈接查看也是一樣的:

pytest是一個測試框架,功能與unittest類似,完全兼容unittest的功能。一般做接口測試的時候,以前用的多的是python+requests+httptestrunner完成接口自動化測試與報告生成,看到現在很多都在用pytest框架,我也來學習一下,接口測試中pytest應用廣泛的是通過python+pytest+allure生成測試報告,報告格式比較美觀。
文章比較長,先簡單概述一下本文的大概內容:
1、環境搭建以及pytest是怎么運行的,如何識別有效用例
2、用例執行順序、參數傳遞、數據驅動
3、斷言,以及常見的pytest裝飾器
4、測試報告生成,包括自帶的pytest的報告以及集成allure的報告。
pytest安裝
pip install -U pytest
查看安裝版本:
cmd窗口輸入:pytest --version ,會在窗口中輸出類似下面格式的一句話:
This is pytest version 5.4.3, imported from d:\python38\lib\site-packages\pytest\__init__.py
用例的識別與運行
用例編寫規范
-
測試文件以test_開頭(或者以_test結尾)
pytest會找當前以及遞歸查找子文件夾下面所有的test_*.py或*_test.py的文件,把其當作測試文件(除非顯式指定文件所在路徑)
-
測試類名稱以Test開頭,並且不能帶有init方法
如果類名稱以Test開頭的class類中包含了init方法,則會觸發告警,提示PytestCollectionWarning: cannot collect test class 'TestXXX'
-
測試函數以test_開頭
-
斷言使用基本的assert即可
運行參數
你們可能會有這樣的疑問,現在大家都在用類似pycharm的IDE工具,為什么還要去學習命令行運行的參數和方式呢?
pytest框架是一個測試框架,如果需要集成到jenkins上的話,是需要用命令行的方式去執行的,有時候要執行多個用例的時候,用命令行文件比較方便。
pytest可以在命令行執行,在命令行執行的時候,可以帶很多參數,下面介紹幾種常用到的參數用法:(使用pytest --help可以看到命令參數的幫助文檔)
-
不帶參數執行
使用方法:pytest 或者 py.test , 將會讀取當前路徑下所有符合規則的文件,類,方法,函數全部執行
-
-v 參數
打印詳細運行的日志信息,方便定位問題
-
-s參數
可以在控制台輸出結果,當代碼中有用到print語句輸出信息時,不加這個參數的話,控制台是不會顯示print的內容的
-
-k參數
使用該參數可以指定運行滿足要求的用例。用法如下:
pytest -k "類名"pytest -k "方法名"pytest -k "類名 and not 方法名"
注意: -k參數后面跟的引號只能用雙引號"",不能用單引號'',否則不會識別到用例,運行會報錯
-
-x參數
遇到用例執行失敗或斷言失敗,立即停止運行,不執行后面的用例。
-
--maxfail參數
設置允許失敗的用例數,超過這個閾值時,停止運行。
pytest --maxfail=num ,失敗用例數>=num時,停止運行
-
-m參數
按照標簽名運行所有包含某個標簽的用例,需要在測試用例上面都加上裝飾符@pytest.mark.標記名。使用-m選項,可以使表達式指定多個標記名。使用-m "mark1 and mark2"可以同時選中帶有這兩個標記的所有測試用例。使用-m "mark1 and not mark2"則會選中帶mark1標記且不帶mark2標記的測試用例,使用-m "mark1 or mark2"則會選中帶有mark1或者mark2的所有測試用例。
用例標記使用用法如下:
import pytest@pytest.mark.mark1@pytest.mark.mark2def test_a002(self):print('this is test_a002 method')
使用-m參數運行時,有可能會提示
PytestUnknownMarkWarning: Unknown pytest.mark.xxx - is this a typo?
這只是一個告警,不影響實際執行結果。處理方式有以下幾種:
方法一:在測試用例文件的根目錄新建conftest.py,內容如下:
# 單標簽處理方式def pytest_configure(config):config.addinivalue_line("markers", "mark1" # test 是標簽名)# 多標簽處理方式def pytest_configure(config):marker_list = ["mark1", "mark2"] # 標簽名集合for markers in marker_list:config.addinivalue_line("markers", markers)
方法二:在項目根路徑或者用例目錄下新建一個pytest.ini文件,內容如下:
[pytest]markers=mark1mark2mark3或者用如下格式:[pytest]markers=mark1:this is test1 method markmark2:this is test2 method markmark3:this is test3 method mark
注意:有多個mark的時候,換行寫,要注意縮進,不縮進是無法識別的,每個mark名稱后面是可以加冒號,然后備注mark的相關詳細描述信息。
方法三:在pytest.ini文件中設置告警過濾,這樣可以避免由於mark標記使用過多時,要一個一個配置,比較麻煩。
具體使用方法可以參考官方文檔:
https://docs.pytest.org/en/latest/warnings.html
[pytest]addopts = -p no:warnings或者:[pytest]filterwarnings =errorignore::UserWarning
運行模式
pytest提供了多種運行模式,可以指定某個模塊,執行單個pytest模塊進行調試,也可以單獨運行某個類下面的某個測試方法。
命令行運行具體使用方法如下:
pytest 文件名.pypytest 文件名.py::類名pytest 文件名.py::類名::方法名
也可以在pycharm中運行pytest用例
1、先打開Pycharm設置->Tools->Python Integrated Tools->Testing:pytest
(需要安裝pytest依賴,然后符合編寫規則的測試用例都能被pycharm識別出來,會在用例前面出現一個綠色的執行按鈕,點擊這個按鈕就能執行某個方法或者某個類)


pytest 框架結構
與unittest類似,執行用例前后會執行setup、teardown來增加用例的前置和后置條件。
pytest的前置和后置條件大概有這么幾類:
-
模塊級(setup_module/teardown_module)
在模塊始末調用
-
函數級(setup_function/teardown_function)
在函數始末調用(在類外部)
-
類級(setup_class/teardown_class)
在類始末調用(在類中)
-
方法級(setup_method/teardown_method)
在方法始末調用(在類中)
-
方法級(setup/teardown)
在方法始末調用(在類中)
調用順序:
setup_module>setup_function>teardown_function>setup_class>setup_method>setup>teardown>teardown_method>teardown_class>teardown_module
注意事項:
1、其中函數級的setup_function/teardown_function是在class類外部調用的,寫在class類中是沒用的,不會調用
2、(setup_method/teardown) 與 (setup/teardown)功能是一樣的,優先級是先執行setup_method,在執行setup。一般二者用其中一個即可.
驗證上面的執行順序,可以執行下面的腳本,
在一個test開頭的py文件里面,編寫一下腳本:def setup_module():print('\n 這是setup_module方法,只執行一次,當有多個測試類的時候使用')def teardown_module():print('這是 teardown_module方法,只執行一次,當有多個測試類的時候使用')def teardown_module():print('這是 teardown_module方法,只執行一次,當有多個測試類的時候使用')def setup_function():print('這是 setup_function方法,只執行一次,當有多個測試類的時候使用')def teardown_function():print('這是 teardown_function方法,只執行一次,當有多個測試類的時候使用')def test_five():print('this is test_five method')def test_six():print('this is test_six method')class TestPytest01:def setup_class(self):print('調用了setup_class1方法')def teardown_class(self):print('調用了teardown_class1方法')def setup_method(self):print('執行測試方法前的setup1操作')def teardown_method(self):print('執行測試方法后的teardown1操作')def test_one(self):print('this is test_one method')def test_two(self):print('this is test_two method')def setup(self):print('this is setup 方法')def teardown(self):print('this is teardown 方法')class TestPytest02:def setup_class(self):print('調用了setup_class2方法')def teardown_class(self):print('調用了teardown_class2方法')def setup_method(self):print('執行測試方法前的setup2操作')def teardown_method(self):print('執行測試方法后的teardown2操作')def test_three(self):print('this is test_three method')def test_four(self):print('this is test_four method')
然后看打印的輸出結果:

控制用例的執行順序
pytest默認的執行順序是按照文件名以及測試方法名稱排序執行的,如果想指定用例的順序,可以使用pytest-ordering插件,在測試方法前面加上裝飾器@pytest.mark.run(order=num),就可以按照num的大小順序來執行。
安裝:
pip install pytest-ordering
案例:
import pytestclass TestPytest:@pytest.mark.run(order=-2)def test_03(self):print('test_03')@pytest.mark.run(order=-3)def test_01(self):print('test_01')@pytest.mark.run(order=4)def test_02(self):print('test_02')
執行結果如下:

注意:按照num排序時,正整數在前,負數在后面,然后統一按照從小到大的順序執行。(我目前使用的是pytest5.4.3版本,不排除以后版本更改排序規則)
pytest fixtures
pytest中可以使用@pytest.fixture裝飾器來裝飾一個方法,被裝飾方法的方法名可以作為一個參數傳入到測試方法中。可以通過這種方式來完成測試之前的初始化操作,也可以返回數據給測試函數。
import pytestclass TestFixture:@pytest.fixture()def login(self):return 11def test_001(self, login):assert 1+10 ==login
fixture的scope參數:
根據作用范圍大小范圍划分,分別是:session>module>class>function.
@pytest.fixture(scope='function') scope的默認值是function
-
function函數或者方法級別都會被調用
-
class類級別調用一次
-
module模塊級別調用一次
-
session是多個文件調用一次,可以跨.py文件調用,每個.py文件就是module
通過以下腳本可以測試一下scope的作用范圍:
通過更改scope的枚舉值,即可看到效果,可以看到print('調用login方法')在不同的scope選項下,打印出來的次數是不一樣的。
import pytest@pytest.fixture(scope='class')def login():print('調用login方法')return 11class TestFixture:def test_001(self, login):print('this is test_001方法')assert 1+10 ==logindef test_002(self, login):print('this is test_001方法')assert 1+10 ==loginclass TestFixture1:def test_003(self, login):print('this is test_003方法')assert 1+10 ==login
conftest.py文件
一般用於scope='session'級別的fixture。conftest.py被pytest視為一個本地插件庫,使用conftest.py的規則:
1、conftest.py這個文件名是固定的,不可以更改
2、conftest.py與運行用例在同一個包下,並且該包中要有__init__.py文件
3、使用的時候不需要導入conftest.py,pytest會自動加載,放到哪個package下,就在這個package內有效。
fixture的autouse參數:
如果想讓每條測試用例都添加fixture功能,那么可以使用@pytest.fixture里面的autouse參數,autouse='true'則會自動應用到所有用例中。
用法如下:

使用fixture傳遞測試數據
fixture的param參數可以用來傳遞測試數據,實現數據驅動的效果,避免出現冗余代碼。可以大大減少代碼量,並且便於閱讀和維護。傳入的數據需要使用一個固定的參數名request來接收,代碼如下:
import pytest@pytest.fixture(params={1,2,3})def data(request):return request.paramdef test_data(data):print("測試數據{data}")assert data<10
運行結果:

pytest使用pytest-xdist並行運行測試
pytest-xdist是pytest里面的一個分布式執行的插件,可以多個CPU或主機執行。這個是進程級的並發,需要保證測試用例之間的獨立性,插件是動態決定測試用例執行順序,如果互相之間有依賴,可能會導致執行失敗/達不到預期的結果。
安裝:pip install pytest-xdist
用法:
pytest -n auto 或者 pytest -n num
參數為auto時,系統會自動檢測CPU核數,如果參數為num數字的話,則表示指定運行的處理器進程數量。
pytest使用pytest-html生成簡易測試報告
安裝:pip install pytest-html
使用方法:pytest --html=xxxx/report.html (通過這種方式,生成的html報告,css文件是獨立的,發給其他人的時候要一起帶上css樣式文件)
pytest --html=xxxx/report.html --self-contained-html (使用self-contained-html 參數,會將css樣式文件的內容直接寫到html文件中)
生成的報告樣式如下:

報告中會包含Environment和Summary以及Results的相關數據,如果想要在Environment和Summary下添加一些個性化的內容展示到報告中的話,可以在conftest.py文件中添加以下代碼:
import pytestfrom py._xmlgen import html# Environment下添加配置項或者修改已有配置項d的值def pytest_configure(config):config._metadata['測試地址'] = '192.168.1.XXX'config._metadata['項目描述'] = '這是XXX項目測試報告'config._metadata['Python'] = '2.7' #報告中默認會有python版本,可以自己手動修改# Summary下添加個性化的內容展示@pytest.mark.optionalhookdef pytest_html_results_summary(prefix, summary, postfix):prefix.extend([html.p("測試人: 小博")])
加上以上代碼后,運行生成的報告如下:

pytest斷言
使用過unittest框架的都知道,unittest里面封裝了很多的斷言方法,有assertEqua、assertNotEqual等好幾十個斷言的方法,在pytest中,斷言直接使用assert關鍵字就行:
assert xx:判斷xx為真
assert not xx:判斷xx不為真
assert a in b:判斷b是否包含a
assert a == b:判斷a等於b
assert a !=b:判斷a不等於b
斷言要做什么判斷,可以自己去定義。也可以在assert后面加上斷言失敗后的描述信息:
assert a>b,'斷言失敗,實際結果是a<b'
pytest parametrize參數化
先來看一下parametrize()的方法源碼:
def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None):
-
主要參數說明:
argsnames:參數名,是一個字符串,多個參數名中間可以用逗號分隔
argsvalues:參數對應的值,是由參數組成的列表,列表中有幾個元素,就會生成幾條用例,參數名和參數值的數量要相等。
indirect:該參數值默認為False,表示argnames就是普通的參數,如果將該值設置為True,則可以用來將參數傳入到fixture方法中。
ids:用於標志用例的一個id字段,默認可以不傳,會自動用argvalues填充,ids參數可以用來區分測試方法的標識。
scope:聲明argnames中參數的作用域,進而影響到測試用例的收集順序
-
parametrize使用方法:
單個參數:
@pytest.mark.parametrize('a',[1,2,3,4] )def test_ddt01(a):assert a<5
輸出結果:
多個參數:
@pytest.mark.parametrize('a,b',[("1+1",2),("1+2",3)])def test_ddt02(a,b):assert eval(a) == b

多次使用parametrize的用法:
對同一個方法使用多次@pytest.mark.parametrize裝飾器
@pytest.mark.parametrize('a',[1,2])@pytest.mark.parametrize('b',[1,2])def test_ddt03(a,b):print(f'數據組合 a:{a}, b:{b}')

ids參數用法及效果:
@pytest.mark.parametrize('a',[1,2,3 ],ids=('id-1','id-2','id-3' ))def test_ddt04(a):assert a<5

indirect用法:
使用indirectTrue,pytest可以實現將參數傳入到fixture方法中,也可以在當前測試用例中使用。
def fun_a(request):print(f'fun_a:{request.param}')return request.param# indirect=True 聲明fun_a是個函數def test_ddt05(fun_a):print(f'a:{fun_a}')assert fun_a<10

scope參數用法及結果演示:
import pytest@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4),(5,6)], scope='module')def test_scope1(test_input, expected):pass@pytest.mark.parametrize('test_input, expected', [(1, 2), (3, 4),(5,6)], scope='module')def test_scope2(test_input, expected):pass


pytest結合YAML實現數據驅動
在實際測試工作中,通常需要對多組不同餓的輸入數據,進行同樣的測試操作步驟,可以將多組測試數據以數據驅動的形式注入,可以做到測試數據和測試用例分別進行管理。常見的外部數據源可以用YAML、Json、Excel、CSV等方式進行管理。
下面以YAML為例,簡單演示一下如何實現數據驅動:
安裝: pip install PyYAML
案例:
創建一個testdata的文件夾,在下面創建data.yml和test_yaml.py文件,內容如下:
data.yaml:-- 1- 2-- 20- 30
test_yaml.py:import pytestimport yaml@pytest.mark.parametrize('a,b',yaml.safe_load(open('data.yml',encoding='utf-8')))def test_add(a,b):print(f'a+b = {a+b}')
輸出結果:

pytest結合allure生成測試報告
Allure框架是一種靈活的、輕量級、支持多語言的測試報告工具,報告美觀清晰、一目了然。同時支持多種語言,包括Java、Python、JavaScript、Ruby、Groovy、PHP、.Net、Scala等。
環境搭建:
1、以windows系統為例(先安裝好JDK並配置環境變量),先下載allure的命令行工具進行安裝。下載地址可從github上進行下載:
https://github.com/allure-framework/allure2/releases
github如果下載過慢的話,可以從我的網盤里面下載:
鏈接:https://pan.baidu.com/s/1CJvL_jJuJhFAFP0olUnlCA
提取碼:1234
下載最新的安裝包后,解壓,配置環境變量。
新建一個ALLURE_HOME的環境變量,value指向解壓后的根路徑,,我電腦上的是:G:\devops\allure-2.9.0

然后在PATH中加入%ALLURE_HOME%\bin
之所以要單獨配置解壓后的路徑為ALLURE_HOME,是為了以后更換版本后更改環境變量比較方便。
配置好后,在cmd窗口輸入 allure --version 會打印出安裝的版本。

2、安裝python的allure-pytest插件
pip install -U allure-pytest
具體使用方法:
步驟一:在會用pytest執行用例的時候,指定參數 --allure選項及結果數據保存的目錄:
pytest --alluredir=./tempdir/data
pytest --alluredir=./tempdir/data --clean-alluredir
加--clean-alluredir選項會先清理數據目錄,再重新生成新的數據,不清理也不會影響報告的生成。
步驟二:
-
使用allure serve 打開報告:
在cmd窗口輸入allure serve ./tempdir/data ,就會自動打開瀏覽器顯示報告:

-
使用allure ganerate命令生成html格式報告
cmd窗口輸入如下命令:
allure generate ./tempdir/data -o ./report --clean
命令說明:
./tempdir/data 指測試數據目錄, ./report 指html報告生成的位置, --clean指先清空測試報告目錄再重新生成新的測試報告。
需要使用下面的命令打開報告,直接打開html文件,看不到數據:
allure open -h 127.0.01 -p 8088 ./report/
到此,allure報告就生成了,至於報告怎么去分析和查看,可以將報告切換為中文版本自己去進行分析即可。
以上就是pytest常見的一些用法,適合新手入門了解,后續有時間會繼續補充pytest的一些其他語法和用法以及擴展功能,歡迎關注小編,能及時獲取下次更新喔!
如果覺得這篇文章對你有幫助,請分享給身邊的朋友一起學習,謝謝!
