????本文參考了官方文檔和一些樂于分享的大佬的博客,結合自己的理解完成。學習pytest框架的小白,需要按照教程自己敲一遍,配置一遍,摸索一下整個框架的運行邏輯,數據流的走向,文字雖多,請細細看完,有問題歡迎在群里提出,相互學習,互相指正。希望大家有所收獲,學有所得。(群:自動化測試-夜行者:816489363)
--成都-阿木木
框架說明
官方文檔:https://docs.pytest.org/en/latest/contents.html
感謝慕城南風的博客:https://blog.csdn.net/lovedingd/article/details/98952868
Pytest支持的插件庫:https://plugincompat.herokuapp.com/
第三方插件庫:https://docs.pytest.org/en/latest/plugins.html
pytest兼容unittest
pytest兼容以前的unittest,只需要少量的更改代碼即可,下面就大家熟悉的setup、teardown以及html報告進行說明
setup和teardown
setup和teardown主要分為:類級、函數級。
運行于測試方法前后:
#!/user/bin/env?python
#?-*-?coding:?utf-8?-*-
"""??
------------------------------------??
@Project?:?pyqt5_study??
@Time????:?2020/8/4?9:33??
@Auth????:?chineseluo??
@Email???:?848257135@qq.com??
@File????:?test_setup_teardown.py??
@IDE?????:?PyCharm??
------------------------------------??
"""??
import?pytest??
class?TestSetupTeardown():??
????def?setup(self):??
????????print("運行于測試方法之前")??
????def?teardown(self):??
????????print("運行于方法之后")??
????def?test_01(self):??
????????print("這是第一個方法")??
????def?test_02(self):??
????????print("這是第二個方法")??
if?__name__?==?'__main__':??
????pytest.main("-s?test_setup_teardown.py")??
運行結果:
test_setup_teardown.py::TestSetupTeardown::test_01???
運行于測試方法之前??
PASSED????????????????[?50%]這是第一個方法??
運行于方法之后??
test_setup_teardown.py::TestSetupTeardown::test_02???
運行于測試方法之前??
PASSED????????????????[100%]這是第二個方法??
運行于方法之后??
運行于測試類的始末:
#!/user/bin/env?python
#?-*-?coding:?utf-8?-*-
"""?
------------------------------------??
@Project?:?pyqt5_study??
@Time????:?2020/8/4?9:33??
@Auth????:?chineseluo??
@Email???:?848257135@qq.com??
@File????:?test_setup_teardown.py??
@IDE?????:?PyCharm??
------------------------------------??
"""??
import?pytest??
class?TestSetupTeardown():??
????@classmethod??
????def?setup_class(self):??
????????print("運行于測試類之前")??
????@classmethod??
????def?teardown_class(self):??
????????print("運行于測試類之后")??
????def?test_01(self):??
????????print("這是第一個方法")??
????def?test_02(self):??
????????print("這是第二個方法")??
if?__name__?==?'__main__':??
????pytest.main(["-s","test_setup_teardown.py"])??
html測試報告
使用pytest的測試報告插件可以替換unittest本身的HTMLTestRunner報告
安裝:pip install pytest-html
使用方式:命令行格式:pytest --html=用戶路徑/report.html
pytest框架使用約束
所有的單測文件名都需要滿足test_*.py格式或*_test.py格式。
在單測文件中,測試類以Test開頭,并且不能帶有?init?方法(注意:定義class時,需要以T開頭,不然pytest是不會去運行該class的)
在單測類中,可以包含一個或多個test_開頭的函數。
此時,在執行pytest命令時,會自動從當前目錄及子目錄中尋找符合上述約束的測試函數來執行。可以在pytest.ini中修改測試目錄、測試模塊、測試類、測試方法掃描進行默認修改。
Pytest Exit Code含義清單
程序運行成功結束控制臺輸出:Process?finished?with?exit?code?0??
Exit code 0 所有用例執行完畢,全部通過
Exit code 1 所有用例執行完畢,存在Failed的測試用例
Exit code 2 用戶中斷了測試的執行
Exit code 3 測試執行過程發生了內部錯誤
Exit code 4 pytest 命令行使用錯誤
Exit code 5 未采集到可用測試用例文件
pytest之fixture
Fixture作用
fixture修飾器來標記固定的工廠函數,在其他函數,模塊,類或整個工程調用它時會被激活并優先執行,通常會被用于完成預置處理和重復操作。
fixture是在測試函數前后運行,由pytest執行的外殼函數;代碼可以定制,滿足多變的測試需求,包括定義傳入測試中的數據集,配置測試前系統的初始工作,為批量測試提供數據源等等,fixture是pytest用于將測試前后進行預備,清理工作的代碼分離出核心測試邏輯的一種機制
說明
@pytest.fixture()裝飾器用于申明函數是一個fixture,如果測試函數的參數列表中包含fixture,那么pytest會檢測到,檢測順序是,優先搜索該測試所在的模塊,然后搜索conftest.py,并在測試函數運行之前執行該fixture,fixture可以完成測試任務,也可以返回測試數據給測試函數
scope:被標記方法的作用域
function" (default):作用于每個測試方法,每個test都運行一次
"class":作用于整個類,每個class的所有test只運行一次
"module":作用于整個模塊,每個module的所有test只運行一次
"session:作用于整個session(慎用),每個session只運行一次
params:(list類型)提供參數數據,供調用標記方法的函數使用
autouse:是否自動運行,默認為False不運行,設置為True自動運行
pytest --setup-show test_example1.py(可以看到執行過程順序)
3、測試數據返回(參數化)
A.返回測試數據
#?coding:utf-8
import?pytest??
#?@pytest.fixture()不傳參,默認是function級別的,也就是只在test開頭函數前后執行
@pytest.fixture()??
def?fixture_test():??
????print("方法執行前執行")??
????yield??
????print("方法執行后執行")??
def?test_data(fixture_test):??
????assert?2?==?2??
返回測試數據
#?coding:utf-8
import?pytest??
#?@pytest.fixture()不傳參,默認是function級別的,也就是只在test開頭函數前后執行;也可以使用fixture返回數據
@pytest.fixture()??
def?fixture_test():??
return?[1,?2,?3,?4]??
def?test_data(fixture_test):??
????print(fixture_test[1])??
????assert?2?==?fixture_test[1]??
4、Fixture函數存放位置
單個測試模塊文件內,只有該模塊文件的類和方法可以訪問到該fixture函數
如果希望多個測試文件共享fixtrue,可以在某個公共目錄下新建一個fixture,將fixture放在里面
Fixture作用范圍
function級別作用域
function每個函數或方法都會調用(有兩種寫法,不傳遞參數,默認就是function,也可以指定scope="function",來進行作用域的指定
@pytest.fixture()??
def?fixture_function():??
????print("fixturetest測試1")??
? ??return?1??
@pytest.fixture(scope="function"):??
def?fixture_function():??
????print("fixture測試2")??
? ??return?2??
def?test_fixture(fixture_function1):??
????assert?1?==?fixture_function1??
Class級別作用域
#?coding:utf-8
import?pytest??
#?@pytest.fixture(scope="class")只在類的前后執行一次
@pytest.fixture(scope="class")??
def?fixture_class():??
????print("類前執行一次")??
????yield??
????print("類后執行一次")??
class?TestCase:??
????def?test_1(self,?fixture_class):??
????????print("類方法")??
Module級別作用域
#?coding:utf-8
import?pytest??
@pytest.fixture(scope="module")??
def?fixture_module():??
????print("模塊執行前執行")??
????yield??
????print("模塊執行后執行")??
def?test_1(fixture_module):??
????print("\n測試方法")??
class?TestCase:?
????def?test_2(self,?fixture_module):??
????????print("\n類方法")??
Session級別作用域
session是多個文件調用一次,可以跨越.py文件調用,每個.py文件都是module
當我們有多個.py文件的用例時,如果多個用例只需要調用一次fixture,可以設置scope="session",并且寫入到conftest。py文件里面
import?pytest??
@pytest.fixture(scope="session")??
def?fixture_session():??
????print("全局前執行一次")??
????yield??
????print("全局后執行一次")??
#?coding:utf-8
import?pytest??
def?test_1(fixture_session):??
????print("方法")??
class?TestCase:??
????def?test_2(self,?fixture_session):??
????????print("類方法")??
pytest之配置文件
pytest非測試文件介紹
1、pytest.ini:pytest的主配置文件,可以改變pytest的默認行為,其中有很多可以配置的選項,包含日志,命令行的一些參數,控制臺輸出的信息等等
2、conftest.py:是本地的插件庫,其中的hook函數和fixture將作用于該文件所在目錄以及所有子目錄
如何查看pytest.ini選項
使用pytest --help查看pytest.ini所有設置選項
如何更改默認命令行選項
pytest -v --verbose 可以輸出詳細信息
[pytest]
addops = -v --alluredir ./allure-result(addopts增加默認執行的操作步驟,簡化命令行參數)(allure測試報告默認在json文件目錄下生成,可以使用allure generate jsonpathdir -o allurepathdir更改)
ps:
如何使用allure生成測試報告
1、brew install allure
2、安裝allure-pytest
3、運行case時增加命令行選項pytest -v --allure ./allure-results test.py
4、生成測試報告allure generate allure-results -o allure
有哪些常用的命令行選項呢?
-v:輸出詳細信息,顯示具體執行了那些測試用例
--collect-only 展示在給定的配置下那些測試用例會被執行,僅用于展示,不執行
-k 允許使用表達式指定希望運行的測試用例
exp:pytest -v -k 'baidu' test.py(在pytest中查找含有baidu關鍵字的case執行)
-m marker用于標記測試并分組
--strict 遇到mark拼寫錯誤會檢查,與mark配合使用
注冊標記防范拼寫錯誤
自定義標記可以簡化測試工作,但是標記容易拼寫錯誤,默認情況下不會引起錯誤,pytest以為這是另外一個標記,為了避免拼寫錯誤,可以在pytest.ini文件里進行注冊
markers = data_file:a test_data get_and_format marker
通過命令查看:pytest --help(或者pytest --marks)沒有注冊的標記不會出現在markers列表里面,如果使用--strict選項,遇到拼寫錯誤的標記或者未注冊的標記會報錯
如果自己增加一個測試函數的標記呢?
@pytest.mark.smoke
pytest -m 'smoke' test.py
執行pytest的最低版本號設置
minversion = 6.0
minversion選項可以指定運行測試用例的pytest的最低版本
指定pytest忽略某些目錄
norecursedirs = .*data config utils
可以使用norecursedirs縮小pytest的搜索范圍
指定訪問目錄
testpath = testsdir
配置日志
通過將log_cli配置選項設置為true,pytest將在直接將日志記錄發送到控制臺時輸出日志記錄。
您可以指定傳遞的級別,以將等于或更高級別的日志記錄打印到控制臺的日志記錄級別--log-cli-level。此設置接受python文檔中顯示的日志記錄級別名稱,或者接受整數作為日志記錄級別num。
此外,您還可以指定--log-cli-format和?--log-cli-date-format哪個鏡和默認--log-format和?--log-date-format如果沒有提供,但只被應用到控制臺日志處理程序。
還可以在配置INI文件中設置所有CLI日志選項。選項名稱為:
log_cli_level
log_cli_format
log_cli_date_format
如果您需要將整個測試套件的日志記錄記錄到一個文件中,則可以傳遞?--log-file=/path/to/log/file。該日志文件以寫模式打開,這意味著它將在每個運行測試會話中被覆蓋。
您還可以通過傳遞日志文件的日志記錄級別?--log-file-level。此設置接受python文檔中所見的日志記錄級別名稱(即大寫的名稱),或者接受整數作為日志記錄級別num。
此外,您還可以指定--log-file-format和?--log-file-date-format,它們等于--log-format和?--log-date-format但應用于日志文件日志處理程序。
還可以在配置INI文件中設置所有日志文件選項。選項名稱為:
log_file
log_file_level
log_file_format
log_file_date_format
您可以調用set_log_path()以動態自定義log_file路徑。此功能被認為是實驗性的。
[pytest]??
testpaths?=?TestCases??
log_format?=?%(asctime)s?%(levelname)s?%(message)s??
log_level?=?INFO??
log_file_level?=?debug??
log_file_date_format?=?%Y-%m-%d?%H:%M:%S??
log_file_format?=?%(asctime)s?%(levelname)s?%(message)s??
;log_file?=?../../Logs/log.log??
log_cli?=?True??
log_cli_level?=?INFO??
log_cli_format?=?%(asctime)s?[%(levelname)1s]?%(message)s?(%(filename)s:%(lineno)s)??
log_cli_date_format=%Y-%m-%d?%H:%M:%S??
8、配置例子
#配置pytest命令行運行參數
[pytest]
addopts = -s ... # 空格分隔,可添加多個命令行參數 -所有參數均為插件包的參數配置測試搜索的路徑
testpaths = ./scripts # 當前目錄下的scripts文件夾 -可自定義
#配置測試搜索的文件名稱
python_files = test*.py
#當前目錄下的scripts文件夾下,以test開頭,以.py結尾的所有文件 -可自定義
配置測試搜索的測試類名
python_classes = Test_*
#當前目錄下的scripts文件夾下,以test開頭,以.py結尾的所有文件中,以Test開頭的類 -可自定義
配置測試搜索的測試函數名
python_functions = test_*
#當前目錄下的scripts文件夾下,以test開頭,以.py結尾的所有文件中,以Test開頭的類內,以test_開頭的方法 -可自定義
pytest之數據驅動
Fixture
import?pytest??
@pytest.fixture(params=[1,?2,?3])??
def?need_data(request):?#?傳入參數request?系統封裝參數??
return?request.param?#?取列表中單個值,默認的取值方式??
class?Test_ABC:??
????def?test_a(self,need_data):??
????????print("------->test_a")??
????????assert?need_data?!=?3?#?斷言need_data不等于3??
if?__name__?==?'__main__':??
????pytest.main("-s??test_abc.py")??
parametrize裝飾器
@pytest.mark.parametrize(argnames,argvalues)裝飾器可以達到批量傳送參數的目的,argvalues里面傳遞的是元組或者列表里面嵌套元組的方式
pytest插件與hook函數
簡介
pytest可以通過添加插件可以擴展功能,pytest的代碼結構適合定制和擴展插件,可以借助hook函數來實現。把fixture函數或者hook函數添加到conftest文件里,這種方式,就已經創建了一個本地的conftest插件!!!
pytest plugin加載的幾種方式
1、內置plugins:從代碼內部的_pytest目錄加載;
2、外部插件(第三方插件):通過setuptools entry points機制發現的第三方插件模塊;
推薦使用的第三方的pytest插件:https://docs.pytest.org/en/latest/plugins.html
3、conftest.py形式的本地插件:測試目錄下的自動模塊發現機制
通過pytest --trace-config命令可以查看當前pytest中所有的plugin
在pytest中,所謂的plugin其實就是能被pytest發現的一些帶有pytest hook方法的文件或者對象
什么是hook方法(鉤子函數)
使用的框架提供公用的規則,其他開發者使用這個規則編寫的文件或者代碼可以被框架識別,框架進行初始化時,會收集滿足這個規則的所有代碼(文件),然后將這些代碼加入到框架中來,在執行時,一并進行初始化。所有這一規則下可以被框架收集到的方法就是hook方法。
編寫自己的插件
插件可以改變pytest行為,可用的hook函數很多,詳細的定義:
http://doc.pytest.org/en/latest/_modules/_pytest/hookspec.html
1、pytest_addoption,基本每個pytest plugin都會有這個hook方法,它的作用是為pytest命令添加自定義的參數
parser:用戶命令行參數與ini文件值的解析器
def pytest_addoption(parser):
parser.addoption("--env",##注冊一個命令行選項
default="test",#默認值為test
dest="env",
help="set test run env")#說明
pytest_addoption:Hook function,這里創建了一個argparser的group,通過addoption方法添加option,使得顯示help信息時相關option顯示在一個group下面,更加友好,使用pytest --help可以查看
def?pytest_addoption(parser):??
????group?=?parser.getgroup("chinese?auto?test")??
????group.addoption("--env",?default="ggg",?dest="env",?help="test?env")??
????group.addoption("--env2",?default="ggg",?dest="env",?help="test?env")??
@pytest.fixture(scope="session")??
def?cmdopt(request):??
????print("獲取不同環境變量的配置")??
return?request.config.getoption("--env")??
2、pytest_collection_modifyitems,是一個完成所有測試項的收集后,pytest調用的鉤子
def pytest_collection_modifyitems(items):
pass
測試用例收集完成后,將收集到的item的name和nodeid的中文顯示在控制臺上,所有的測試用例收集完畢后調用,可以再次過濾或者對它們重新排序
items(收集的測試項目列表)
def?pytest_collection_modifyitems(items):??
????print("test?hook?函數")??
for?item?in?items:??
????????item.name?=?item.name.encode("utf-8").decode("unicode_escape")??
????????item._nodeid?=?item._nodeid.encode("utf-8").decode("unicode_escape")??
Pytest高級用法
1、跳過測試函數
根據特定的條件,不執行標識的測試函數.
方法:
skipif(condition, reason=None)
參數:
condition:跳過的條件,必傳參數
reason:標注原因,必傳參數
使用方法:
@pytest.mark.skipif(condition, reason="xxx")
import?pytest??
class?Test_ABC:??
????def?setup_class(self):??
????????print("------->setup_class")??
????def?teardown_class(self):??
????????print("------->teardown_class")??
????def?test_a(self):??
????????print("------->test_a")??
????????assert?1??
????@pytest.mark.skipif(condition=2>1,reason?=?"跳過該函數")?#?跳過測試函數test_b??
????def?test_b(self):??
????????print("------->test_b")??
????????????assert?0??
2、標記為預期失敗的函數
標記測試函數為失敗函數
方法:
xfail(condition=None, reason=None, raises=None, run=True, strict=False)
常用參數:
condition:預期失敗的條件,必傳參數
reason:失敗的原因,必傳參數
使用方法:
@pytest.mark.xfail(condition, reason="xx")
import?pytest??
class?Test_ABC:??
????def?setup_class(self):??
????????print("------->setup_class")??
????def?teardown_class(self):??
????????print("------->teardown_class")??
????def?test_a(self):??
????????print("------->test_a")??
????????assert?1??
????@pytest.mark.xfail(2?>?1,?reason="標注為預期失敗")?#?標記為預期失敗函數test_b??
???????def?test_b(self):??
???????????print("------->test_b")??
??????????assert?0??
函數參數化
方便測試函數對測試屬于的獲取。
方法:
parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
常用參數:
argnames:參數名
argvalues:參數對應值,類型必須為list
當參數為一個時格式:[value]
當參數個數大于一個時,格式為:[(param_value1,param_value2.....),(param_value1,param_value2.....)]
使用方法:
@pytest.mark.parametrize(argnames,argvalues)
? 參數值為N個,測試方法就會運行N次
在函數參數化中還可以傳遞函數,進行參數化
import?pytest??
def?return_test_data():??
return?[(1,2),(0,3)]??
class?Test_ABC:??
????def?setup_class(self):??
????????print("------->setup_class")??
????def?teardown_class(self):??
????????????print("------->teardown_class")??
@pytest.mark.parametrize("a,b",return_test_data())?#?使用函數返回值的形式傳入參數值??
def?test_a(self,a,b):??
????print("test?data:a=%d,b=%d"%(a,b))??
????assert?a+b?==?3??
4、修改python traceback輸出
pytest --showlocals # show local variables in tracebacks
pytest -l # show local variables (shortcut)
pytest --tb=auto # (default) 'long' tracebacks for the first and last
# entry, but 'short' style for the other entries
pytest --tb=long # exhaustive, informative traceback formatting
pytest --tb=short # shorter traceback format
pytest --tb=line # only one line per failure
pytest --tb=native # Python standard library formatting
pytest --tb=no # no traceback at all
python --full-trace?參數會打印更多的錯誤輸出信息,比參數 --tb=long 還多,即使是 Ctrl+C 觸發的錯誤,也會打印出來
5、獲取用例執行的性能數據
獲取最慢的10個用例的執行耗時
pytest --durations=10
Pytest-xdist進程級并發插件講解
參考夜行者自動化測試群(群號:816489363)文件:pytest-xdist進程級并發參數化說明--成都-阿木木
Pytest常用插件介紹
pytest-assume
多重校驗插件,可以執行完所有的斷言,常規assert斷言執行失敗后下面的斷言便會停止,不在執行,assume插件可以執行完所有斷言
安裝命令:pip install pytest-assume
使用方式:
def?test_add_case(self):??
????pytest.assume(add(1,2)==3)??
????pytest.assume(add(1,4)==3)??
????pytest.assume(add(2,2)==4)??
pytest-ording
Pytest用例默認執行順序是和collect的順序一致,用例收集是按照測試目錄開始,由上到下,在測試模塊中,測試用例收集也是從上到下,需要調整測試函數的執行順序可以通過pytest_collection_modifyitems這個hook函數(鉤子)進行插件編寫。在pytest的第三方插件中,已經有人實現了這個功能,下面介紹pytest-ordering這個插件。
安裝命令:pip install pytest-ordering
@pytest.mark.run(order=2)??
def?test_order1():??
????print?("first?test")??
????assert?True??
@pytest.mark.run(order=1)??
def?test_order2():??
????print?("second?test")??
????assert?True??
pytest-rerunfailures
失敗重跑插件,使用比較簡單,在腳本運行過程中,可能某些原因導致用例執行失敗,可能是網絡加載等,可以使用該插件,對于失敗的用例進行重跑,提高報告的準確性。
安裝命令:pip install pytest-rerunfailures
命令行指定:
Pytest -s test_xxx.py --reruns 5 #表示失敗用例運行五次
Pytest -s test_xxx.py --reruns-delay 2 #表示失敗用例等待2S后在執行
在裝飾器中指定:
@pytest.mark.flaky(reruns=6,?reruns_delay=2)??
????def?test_example(self):??
????????print(3)??
????????assert?random.choice([True,?False])??
pytest-sugar
顯示進度條,控制臺顯示比較好看
顯示效果如下:
?
安裝命令:pip install pytest-sugar
(群:自動化測試-夜行者:816489363)