一、Pytest簡介
1.1 pytest介紹
- pytest是一個非常成熟的python單元測試框架,比unittest更靈活、更容易上手
- pytest可以和selenium、requests、appinum結(jié)合實現(xiàn)web自動化、接口自動化、app自動化
- pytest可以是實現(xiàn)測試用例的跳過以及reruns失敗用例重試
- pytest可以和aliure生成非常美觀的測試報告
- pytest可以和jenkins持續(xù)集成
- pytest有非常強大的插件,并且這些插件能夠?qū)崿F(xiàn)很多的使用的操作
#常用安裝插件
pytest
pytest-html (生成html格式的自動化測試報告)
pytest-xdist (測試用例分布式執(zhí)行,多cpu分發(fā))
pytest-ordering(用于改變測試用例的執(zhí)行順序)
allure-pytest(用于生成美觀的測試報告)
pytest-rerunfailures(用例失敗后重跑)
一般項目作者會把依賴文件附到項目中一起上傳,將這個文件移動到venv文件夾中,打開pycharm中的命令行,這里注意,要在命令行里激活當(dāng)前的虛擬環(huán)境,用的pip才是這個環(huán)境的,pip才會把依賴包下載到這個虛擬環(huán)境里。激活虛擬環(huán)境的方法是cd到venv\Scripts文件夾,輸入activate.bat命令進入虛擬環(huán)境后,cd …退出到venv文件夾(這里放著requirements.txt),然后輸入以下指令:pip install -r requirements.txt等待安裝即可,安裝的依賴包位置在:項目路徑\venv\Lib\site-packages
1.2、Pytest默認規(guī)則
1、模塊名必須以test_開頭或者_test結(jié)尾
2、測試類必須以Test開頭,并且不能有__init__方法
3、測試方法必須以test開頭
二、 Pytest測試用例執(zhí)行方式
2.1 主函數(shù)模式
1、運行所有:pytest.main()
2、指定模塊:pytest.main([‘-vs’,‘test_login.py’])
3、指定目錄:pytest.main([“-vs”,“testcase”])
4、通過nodeid指定用例運行:nodeid由模塊名,分隔符,類名,方法名,函數(shù)名組成
pytest.main([“-vs”,“./interface_testcase/test_interface.py::Testinterface::test_03_zhiliao”])
'''
前置條件:
在test_login.py下創(chuàng)建兩條測試用例
在test_pass.py下創(chuàng)建兩條測試用例
一共4條測試用例
'''
import pytest
class TestLogin:
def test_01(self):
print("測試")
if __name__ == '__main__':
pytest.main(["-vs"])
# pytest.main(["-vs"]) 將會執(zhí)行所有的測試用例,4條測試用例都會執(zhí)行
#pytest.main(["-vs","./test_login.py"] 只會執(zhí)行test_login.py下的兩條測試用例
2.2 命令行模式
1、運行所有:pytest 指定模塊:pytest -vs
2、test_login.py 指定目錄:pytest -vs ./interface_testcase
3、指定nodeid:pytest -vs ./interface_testcase/test_interface.py::Testinterface::test_03_zhiliao
2.3 參數(shù)詳解
-s:表示輸出調(diào)試信息,包括print打印的信息
-v:顯示詳細信息
-vs:一般這兩個參數(shù)一起使用
-n:支持多線程或分布式運行測試用例
例如:pytest -vs ./testcase/test_login.py -n 2
例如:pytest.main(["-vs", "./test_login.py", "-n=2"])
--return:失敗用例重跑
-x:表示只要一個用例報錯,就停止測試
--maxfail=2:出現(xiàn)兩個用例失敗就停止
-k:根據(jù)測試用例的部分字符串指定測試用例
例如:pytest -vs -k "te"
2.4 通過讀取pytest.ini配置文件運行(重點)
pytest.ini這個文件它是pytest單元測試框架的核心配置文件
1、位置:一般放在項目的根目錄
2、編碼:必須是ANSI,可以使用notepad++修改編碼格式
3、作用:改變pytest默認的行為
4、 運行的規(guī)則:不管是主函數(shù)模式,還是命令行模式,都會讀取這個配置文件
#文件名pytest.ini
[pytest]
#命令行參數(shù),用空格分割
addopts = -vs
#測試用例文件夾,可自己配置
testpaths = ./testcase
#配置測試搜索的模塊文件名稱
python_files = test*.py
#配置測試搜索的測試類名
python_classes = Test*
#配置測試搜索的測試函數(shù)名
python_functions = test
三、Pytest執(zhí)行順序
unittest執(zhí)行順序:按照ascli的大小來執(zhí)行
pytest執(zhí)行順序:默認從上到下執(zhí)行
改變pytest的默認執(zhí)行順序:使用@pytest.mark.run(order=1)
'''
需求:將test_02第一個先執(zhí)行
'''
import pytest
class TestLogin:
def test_01(self):
print("測試-1")
@pytest.mark.run(order=1) #.run(order=1) 要手動敲不會自動補全
def test_02(self):
print("測試-2")
四、如何分組執(zhí)行
我們在測試過程中經(jīng)常會有冒煙測試、分模塊測試、分接口測試和web測試等等,那么如何使用pytest進行分組執(zhí)行測試?
在pytest.ini文件中添加markers字段
[pytest]
addopts = -vs
testpaths = ./testcase
python_files = test*.py
python_classes = Test*
python_functions = test
markers =
smoke:冒煙用例
usermanage:用戶管理模塊
productmangage:商品管理模塊
五、Pytest跳過用例
5.1 無條件跳過
無條件跳過: @pytest.mark.skip(reason"跳過")
import pytest
class TestLogin:
@pytest.mark.usermanage
@pytest.mark.skip(reason="跳過")
def test_01(self):
print("測試-1")
@pytest.mark.run(order=1)
@pytest.mark.smoke
def test_02(self):
print("測試-2")
#執(zhí)行結(jié)果:
testcase/test_login.py::TestLogin::test_01 SKIPPED (跳過)
5.2 有條件跳過
有條件跳過: @pytest.mark.skipif(age=18,reason=“年齡等于18則跳過”)
import pytest
class TestLogin:
age = 18
@pytest.mark.usermanage
@pytest.mark.skip(reason="跳過")
def test_01(self):
print("測試-1")
@pytest.mark.skipif(age=18,reason="年齡等于18則跳過")
@pytest.mark.run(order=1)
@pytest.mark.smoke
def test_02(self):
print("測試-2")
#執(zhí)行結(jié)果
testcase/test_login.py::TestLogin::test_02 SKIPPED (年齡等于18則跳過)
interfacecase/test_inter.py::TestPass::test_05 測試-5
PASSED
interfacecase/test_inter.py::TestPass::test_06 測試-6
PASSED
testcase/test_login.py::TestLogin::test_01 SKIPPED (跳過)
testcase/test_pass.py::TestPass::test_03 測試-3
PASSED
testcase/test_pass.py::TestPass::test_04 測試-4
PASSED
======================== 4 passed, 2 skipped in 0.03s =========================
六、前后置(夾具、固件)
6.1 setup/teardown,setup_class/teardown_class實現(xiàn)前后置
為什么要使用前后置?
比如:web自動化執(zhí)行用例之前,請問需要打開瀏覽器嘛?用例執(zhí)行后需要關(guān)閉瀏覽器嘛?
用法:
def setup(self):和def teardown(self): 在每個用例執(zhí)行前后執(zhí)行一次。
def setup_class(self): 和def teardown_class(self): 在當(dāng)前類的所有用例執(zhí)行前后執(zhí)行一次
class TestWeb:
#這個在所有的用例之前只執(zhí)行一次
def setup_class(self):
print('在每個類執(zhí)行前的初始化的工作:比如:創(chuàng)建日志對象,創(chuàng)建數(shù)據(jù)庫的連接,創(chuàng)建接口的請求對象。')
#在每個用例之前執(zhí)行一次。
def setup(self):
print('\n在執(zhí)行測試用例之前初始化的代碼:打開瀏覽器,加載網(wǎng)頁')
def test_01_baili(self):
print('測試-01')
def test_02_xingyao(self):
print('測試-02')
def teardown(self):
print('\n在執(zhí)行測試用例之后的掃尾的代碼:關(guān)閉瀏覽器')
def teardown_class(self):
print('在每個類執(zhí)行后的掃尾的工作:比如:銷毀日志對象,銷毀數(shù)據(jù)庫的連接,銷毀接口的請求對象。')
6.2 使用@pytest.fixture()裝飾器來實現(xiàn)用例的前后置
@pytest.fixture(scope="",params="",autouse="",ids="",name="")
--------------------------------------------------------------------------------
scope:表示的是被@pytest.fixture標記的方法的作用域。function(默認),class,module, package/session
params:參數(shù)化
autouse:默認False autouser=Ture 自動執(zhí)行
ids:當(dāng)使用params參數(shù)化時,給每一個值設(shè)置一個變量,意義不大
name:表示的是被@pytest.fixture標記的方法取一個別名
import pytest
class TestLogin:
age = 18
#部分用例前置,后置,yield表示后置
@pytest.fixture()
def my_fixtre(self):
print("前置")
yield
print("后置")
#將my_fixture函數(shù)傳入,將此用例進行前后置
def test_01(self,my_fixtre):
print("測試-1")
def test_02(self):
print("測試-2")
執(zhí)行結(jié)果:
test_login.py::TestLogin::test_02 測試-2
PASSED
test_login.py::TestLogin::test_01 前置
測試-1
PASSED后置
參數(shù)化:(一般不用,了解)
注:寫在最外層,不在類中填寫,函數(shù)名中傳入request是注定寫法,request.param 返回參數(shù)也是固定寫法
import pytest
@pytest.fixture(scope="function", params=["成龍", "甄子丹", "蔡依林"])
#函數(shù)名中傳入request是注定寫法,request.param 返回參數(shù)也是固定寫法
def my_fixtre(request):
yield request.param #retuen和yield都表示返回的意思,但是yield后面可以跟代碼
print("前置")
class TestLogin:
age = 18
@pytest.mark.usermanage
def test_01(self,my_fixtre):
print("測試-1")
print(my_fixtre)
@pytest.mark.run(order=1)
@pytest.mark.smoke
def test_02(self):
print("測試-2")
#執(zhí)行結(jié)果:
test_login.py::TestLogin::test_02 測試-2
PASSED
test_login.py::TestLogin::test_01[\u6210\u9f99] 測試-1
成龍
PASSED前置
test_login.py::TestLogin::test_01[\u7504\u5b50\u4e39] 測試-1
甄子丹
PASSED前置
test_login.py::TestLogin::test_01[\u8521\u4f9d\u6797] 測試-1
蔡依林
PASSED前置
test_pass.py::TestPass::test_03 測試-3
PASSED
test_pass.py::TestPass::test_04 測試-4
PASSED
6.3 通過conftest.py和@pytest.fixture()結(jié)合使用實現(xiàn)全局的前后置應(yīng)用
使用規(guī)則:
- conftest.py文件是單獨存放的一個夾具配置文件,名稱不能更改
- 用處可以在不同的py文件中使用同一個fixture函數(shù)
- 原則上conftest.py需要和運行的測試用例放到同一層級,并且不需要做任何的import導(dǎo)入的操作
全局的conftest.py文件,在最外層
內(nèi)部的conftest.py文件
用例調(diào)用
執(zhí)行結(jié)果:
testcase/test_login.py::TestLogin::test_02 測試-2
PASSED
testcase/test_login.py::TestLogin::test_01[\u4ed8\u4f1f\u6770-\u6210\u9f99] 測試-1
付偉杰
成龍
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u4ed8\u4f1f\u6770-\u7504\u5b50\u4e39] 測試-1
付偉杰
甄子丹
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u4ed8\u4f1f\u6770-\u8521\u4f9d\u6797] 測試-1
付偉杰
蔡依林
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u5c0f\u5218-\u6210\u9f99] 測試-1
小劉
成龍
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u5c0f\u5218-\u7504\u5b50\u4e39] 測試-1
小劉
甄子丹
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u5c0f\u5218-\u8521\u4f9d\u6797] 測試-1
小劉
蔡依林
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u5c0f\u5b5f-\u6210\u9f99] 測試-1
小孟
成龍
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u5c0f\u5b5f-\u7504\u5b50\u4e39] 測試-1
小孟
甄子丹
PASSED全局前置
用戶管理前置
testcase/test_login.py::TestLogin::test_01[\u5c0f\u5b5f-\u8521\u4f9d\u6797] 測試-1
小孟
蔡依林
PASSED全局前置
用戶管理前置
================================================================= 10 passed in 0.04s =================================================================
七、pytest結(jié)合allure-pytest插件生成allure測試報告
7.1 allure環(huán)境搭建
7.2 生成allure測試報告實例
import os
import pytest
class TestApi:
def test_01(self):
print("測試-1")
def test_02(self):
print("測試-2")
if __name__ == '__main__':
pytest.main(['-vs', '--alluredir', './tmp'])
os.system('allure generate ./tmp -o ./report --clean')
文章來源:http://www.zghlxwxcb.cn/news/detail-742261.html
八、@pytest.mark.parametrize—參數(shù)化
@pytest.mark.parametrize(args_name,args_value)
args_name:參數(shù)名
args_value:參數(shù)值(列表,元祖,字典列表,字典元祖) 有多少個值用例就會執(zhí)行多少次
import pytest
class TestLogin:
age = 18
@pytest.mark.parametrize("args", ["老付", "小劉", "小孟"])
def test_01(self, args):
print("測試-1")
print(args)
def test_02(self):
print("測試-2")
if __name__ == '__main__':
pytest.main()
#執(zhí)行結(jié)果
test_login.py::TestLogin::test_01[\u8001\u4ed8] 測試-1
老付
PASSED
test_login.py::TestLogin::test_01[\u5c0f\u5218] 測試-1
小劉
PASSED
test_login.py::TestLogin::test_01[\u5c0f\u5b5f] 測試-1
小孟
PASSED
test_login.py::TestLogin::test_02 測試-2
PASSED
九、YAML文件詳解–實現(xiàn)接口自動化
9.1 YAML介紹
文章來源地址http://www.zghlxwxcb.cn/news/detail-742261.html
9.2 YAML編寫測試用例常用關(guān)鍵字
feature: 模塊名(必填)
story: 接口名(必填)
title: 用例標題(必填)
request:請求(必填)
method:請求方法(必填)
url:請求路徑(必填)
headers:請求頭
params:url之后的參數(shù)
data:表單數(shù)據(jù)
json:json數(shù)據(jù)
files:文件上傳
extract:
access_token: $.access_token 接口關(guān)聯(lián)提取
vaildate:斷言(必填)
codes:斷言狀態(tài)碼
equals:相等斷言
contains:包含斷言
db_equals: 數(shù)據(jù)斷言
9.3 示例
#test_api.yaml文件-
name: 獲取token鑒權(quán)碼的接口
request:
url: https://www.baidu.com/
method: get
headers:
Content-type: application/json
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
vaildate:
eq: 200
#yaml.util.py文件
import yaml
class YamlApi:
def __init__(self, yaml_file):
"""
通過init方法把yaml文件傳入到這個類
:param yaml_file: yaml文件路徑
"""
self.yaml_file = yaml_file
def write_yaml(self):
"""
讀取yaml,對yaml反序列化,就是把我們的yamk格式轉(zhuǎn)成dict格式
:return:
"""
with open(self.yaml_file, encoding="utf-8") as f:
value = yaml.load(f, Loader=yaml.FullLoader)
print(value, type(value))
return value
if __name__ == '__main__':
YamlApi("./test_api.yaml").write_yaml()
#test_api.py 文件
import pytest
import requests
from testcase.yaml_util import YamlApi
class TestApi:
@pytest.mark.parametrize("args", YamlApi("./test_api.yaml").write_yaml())
def test_10_baidu(self, args):
print(args)
url = args["request"]["url"]
code = args["vaildate"]["eq"]
res = requests.get(url)
code1 = res.status_code
print(code1)
assert code == code1
if __name__ == '__main__':
pytest.main(["-vs", "./test_api.py"])
# 執(zhí)行結(jié)果
test_api.py::TestApi::test_10_baidu[args0] PASSED
============================== 1 passed in 0.46s ==============================
到了這里,關(guān)于全網(wǎng)最全Pytest框架使用教程和項目實戰(zhàn)~從入門到精通的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!