柚子快報激活碼778899分享:pytest框架介紹
一、概述
一)、單元測試框架
1.單元測試框架定義
單元測試框架是在自動化測試或白盒測試中對軟件的最小單元(函數(shù),方法)進行測試的框架。
2.單元測試框架分類
python:unittest,pytest(主流)
Java:Testng(主流),Junit
3.單元測試框架主要做什么
發(fā)現(xiàn)測試用例:從多個文件里面去找到我們的測試用例
執(zhí)行測試用例:按照一定的順序和規(guī)則去執(zhí)行,并生成結(jié)果
判斷測試結(jié)果:通過斷言判斷預(yù)期結(jié)果和實際結(jié)果的差異
生成測試報告:統(tǒng)計測試進度,耗時,通過率,生成測試報告
二)、單元測試框架和自動化測試框架有什么關(guān)系
1.什么是自動化測試框架
為了去完成一個指定的系統(tǒng)的自動化測試而封裝的一整套的完善的代碼的框架。
主要封裝的內(nèi)容:自動化的基礎(chǔ)模塊,自動化的管理模塊,包括統(tǒng)計模塊
2.作用
1).提高測試效率,降低維護成本
2).減少人工干預(yù),提高測試的準確性,增加代碼的重用性
3).核心思想是讓不懂代碼的人也能夠通過這個框架去實現(xiàn)自動化測試
3.Pytest單元測試框架和自動化測試框架的關(guān)系
單元測試框架 是 自動化測試框架的組成部分之一。
pytest 框架是一個 Python 的單元測試框架,支持 Python 函數(shù)、方法和類的測試。
二、Pytest簡介以及常用插件
一).pytest簡介
pytest是一個基于Python的單元測試框架,它支持參數(shù)化、fixture、插件、mock等功能,并且易于使用和擴展。靈活和簡單。
pytest還提供了一個簡單的方式來運行測試用例,并且能夠自動生成測試報告。
可以結(jié)合selinium,requests,appium完成各種不通的自動化。
可以生成自動生成allure報告以及和jenkins持續(xù)集成。
二).常用插件
下面是pytest的一些常用插件:
pytest-xdist:用于多進程或分布式測試。多CPU分發(fā)。pytest-cov:用于測試覆蓋率分析pytest-html:生成HTML測試報告pytest-rerunfailures:用于重試測試失敗的用例pytest-mock:用于mock測試pytest-selenium:用于Selenium瀏覽器自動化測試pytest-ordering :指定測試用例的執(zhí)行順序allure-pytest:生成美觀自定義的HTML報告
pytest-xdist
pytest-xdist插件用于多進程或分布式測試。它可以將測試用例分發(fā)到多個進程或機器上運行,從而加快測試速度。
安裝方法如下:
pip install pytest-xdist
使用方法:
pytest -n
pytest-cov
pytest-cov插件用于測試覆蓋率分析。它可以幫助我們了解測試用例覆蓋了代碼的哪些部分,從而幫助我們優(yōu)化測試用例。
安裝方法如下:
pip install pytest-cov
使用方法:
pytest --cov=
pytest-html
pytest-html插件可以生成HTML測試報告,使測試結(jié)果更加直觀。安裝方法如下:
pip install pytest-html
使用方法:
pytest --html=
pytest-rerunfailures
pytest-rerunfailures插件用于重試測試失敗的用例。它可以幫助我們排除測試環(huán)境的干擾,使測試結(jié)果更加準確。安裝方法如下:
pip install pytest-rerunfailures
使用方法:
pytest --reruns=
pytest-mock
pytest-mock插件用于mock測試。它可以幫助我們模擬測試環(huán)境中的外部依賴,從而使測試結(jié)果更加穩(wěn)定。安裝方法如下:
pip install pytest-mock
使用方法:
# 在測試用例中使用
def test_example(mocker):
mock_func = mocker.Mock(return_value=42)
assert mock_func() == 42
pytest-selenium
pytest-selenium插件用于Selenium瀏覽器自動化測試。它可以幫助我們在測試用例中模擬用戶操作,從而測試網(wǎng)頁應(yīng)用程序。安裝方法如下:
pip install pytest-selenium
使用方法:
plaintext
# 在測試用例中使用
def test_example(selenium):
selenium.get('http://www.baidu.com')
assert '百度' in selenium.title
pytest-ordering
pytest-ordering插件是一個用于pytest的插件,它可以讓我們指定測試用例的執(zhí)行順序。默認情況下,pytest會按照測試用例名稱的字典序進行排序。但是,在某些情況下,測試用例的執(zhí)行順序可能會對測試結(jié)果產(chǎn)生影響,這時我們可以使用pytest-ordering插件來指定測試用例的執(zhí)行順序。安裝方法如下:
pip install pytest-ordering
使用方法:
import pytest
@pytest.mark.run(order=1)
def test_foo():
assert True
@pytest.mark.run(order=2)
def test_bar():
assert True
allure-pytest
allure-pytest插件是一個用于pytest的插件,它可以將pytest的測試結(jié)果轉(zhuǎn)換為美觀的HTML報告,并且支持截圖、日志、環(huán)境變量等功能。allure-pytest插件可以幫助我們更直觀地了解測試結(jié)果,并且方便我們與其他人分享測試報告。安裝方法如下:
pip install allure-pytest
使用方法:
plaintext
pytest --alluredir=allure-report # 生成測試報告
allure serve allure-report # 啟動測試報告服務(wù)器
三).插件安裝
在當前工程根目錄下新建requirements.txt文件,輸入需要安裝的插件,保存
pytest==7.3.1
pytest-html
pytest-xdist
pytest-ordering
pytest-rerunfailures
pytest-cov
pytest-mock
pytest-selenium
allure-pytest
pyyaml
requests
在當前的工程中安裝,每個工程都有虛擬環(huán)境,在虛擬環(huán)境的終端中執(zhí)行命令
pip install -r requirements.txt
終端Terminal 選擇command Prompt,確保在虛擬環(huán)境下運行
(venv) F:\projects\study\python\codes>(工程代碼目錄)
安裝后檢查:打開設(shè)置查看,Settings->Projet:項目名稱->Python Interpreter需要升級的這里可以直接選中,升級
三、測試用例規(guī)則
使用默認規(guī)則:
模塊名必須以test開頭或者_test結(jié)尾。測試類必須以Test開頭,并且不能帶有init方法。測試用例必須以test_開頭。
四、斷言
-assert:斷言一個表達式為True。
- assert equal:斷言兩個值相等。
- assert not equal:斷言兩個值不相等。
- assert greater:斷言第一個值大于第二個值。
- assert less:斷言第一個值小于第二個值。
- assert in:斷言一個值在一個序列中。
- assert not in:斷言一個值不在一個序列中。
五、用例執(zhí)行
一)用例執(zhí)行方式
1.通過命令行執(zhí)行
終端執(zhí)行pytest,輸入pytest即可
pytest -vs # 執(zhí)行所有
pytest -vs test_login.py # 指定模塊,注意切換目錄 cd testcase
pytest -vs ./insterface_testcase # 指定文件夾
pytest -vs ./insterface_testcase/test_interface.py::TestLogin::test03 # 指定測試用例
執(zhí)行的參數(shù)作用前提:安裝插件舉例-vs-v 輸出詳細信息,包括模塊名類名。pytest -vs-s輸出調(diào)試信息,包括print打印的信息-n多線程運行pytest-xdistpytest -vs -n=2–reruns num失敗重跑pytest-rerunfailrespytest -vs --reruns=2raise Exception() 拋出異常try except 解決異常-x出現(xiàn)一個用例失敗停止測試pytest -vs -x–maxfail出現(xiàn)幾個失敗才終止pytest -vs --maxfail=2–html生成html的測試報告pytest-htmlpytest -vs --html ./reports/result.html-k運行測試用例名稱中包含某個字符串的測試用例pytest -vs -k “aa“
2.通過主函數(shù)main方式執(zhí)行
1)運行所有用例
if __name__ == '__main__':
pytest.main(["-vs"]) # 參數(shù)是列表的類型
2)指定模塊運行
if __name__ == '__main__':
pytest.main(["-vs", 'test_login.py'])
3)指定文件夾
if __name__ == '__main__':
pytest.main(["-vs", './interface_testcase'])
通過nodeid制定用例運行:nodeid由模塊名,分隔符,類名,方法名,函數(shù)名組成
if __name__ == '__main__':
pytest.main(["-vs", './interface_testcase/test_interface.py::TestLogin::test03'])
3.通過讀取pytest.ini配置文件執(zhí)行
pytest.ini是pytest單元測試框架的核心配置文件(全局配置文件)。
1.位置:文件放在項目的根目錄下,名稱必須是pytest.ini
2.編碼:文件編碼格式ANSI(utf-8),pycharm中可以設(shè)置加載文件的默認編碼格式,使用notpad++修改編碼格式
3.作用:改變pytest默認的行為/規(guī)則
4.運行的規(guī)則:命令運行或主函數(shù)運行都會加載/讀取這個配置文件
[pytest]
# 命令行參數(shù),用空格分隔
addopts = -vs -m "smoke or model1" # 執(zhí)行冒煙用例和model1模塊的用例
# 測試文件夾,可以自行配置,../pytestproject 上一層的pytestproject文件夾
testpaths = ./testcases
# 測試模塊文件名,搜索所有匹配的文件
python_files = test_*.py
# 測試模塊類名,搜索所有匹配的類
python_classes = Test*
# 測試模塊函數(shù)名,搜索所有匹配的函數(shù)
python_functions = test_*
#標記
markers =
smoke:冒煙測試
model1:model1
model2:model2
4.通過ini文件與主函數(shù)結(jié)合
# pytest.ini
[pytest]
addopts = -vs
testpaths = ./testcases
python_files = test_*.py
python_classes = Test*
python_functions = test_*
markers =
smoke:冒煙測試
login:登錄模塊
h1:用例級別1
h2:用例級別2
h2:用例級別3
# testrun.py
import os
import pytest
from utils.timer import sleep
def run():
pytest.main(['-m h1']) # 每次運行通過-m選擇需要執(zhí)行的模塊
if __name__ == '__main__':
run()
二)用例執(zhí)行順序
默認:從上到下順序執(zhí)行
改變默認的執(zhí)行順序:使用mark標記
@pytest.mark.run(order=1) # 先執(zhí)行帶裝飾器的,后執(zhí)行不帶裝飾器的
三)分組執(zhí)行用例
1.ini文件定義標記
2.測試用例使用裝飾器標記
? 冒煙, 模塊, 接口, web
@pytest.mark.smoke
@pytest.mark.login
@pytest.mark.h1
3.執(zhí)行命令添加 -m “標記”, 命令行執(zhí)行必須是雙引號 參考第五章。
四)Pytest跳過測試用例
(1) 無條件跳過
@pytest.mark.skip(reason='無理由跳過')
# 用例不執(zhí)行
(2) 有條件跳過
@pytest.mark.skipif(key == 0, reason='key為0,跳過')
# 如果key為0,則用例不執(zhí)行,key不為0,則執(zhí)行標記的用例
六、測試用例前后置
一)公共庫實現(xiàn)前后置
實現(xiàn)全局的前后置
定義公共庫,在單獨的文件夾common下新建common_util.py文件,定義公共庫class
class Common:
# 公共工具庫
def setup_class(self):
print('每個類之前執(zhí)行一次')
def teardown_class(self):
print('每個類之后執(zhí)行一次')
def setup(self):
print('每個用例之前執(zhí)行一次')
def teardown_method(self):
print('每個用例之后執(zhí)行一次')
在測試模塊中繼承此類:
class TestModel1(Common):
# 繼承工具類
def test_case1(self):
print('測試用例1')
def test_case2(self):
print('測試用例2')
def test_case3(self):
print('測試用例3')
缺點:不能指定部分用例的前后置
二)fixtrue實現(xiàn)部分前后置
@pytest.fixture(scope=None, autouse=False, params=None, ids=None, name=None)
參數(shù)說明:
1.scope:作用域
? function:在函數(shù)之前和之后執(zhí)行,默認
1.自動調(diào)用方式:在所有測試用例的前后自動調(diào)用
@pytest.fixture(scope='function', autouse=True)
def exe_database_sql():
print("執(zhí)行sql查詢")
yield # 之后的都是后置
print('關(guān)閉數(shù)據(jù)庫連接')
2.手動調(diào)用方式:在測試用例的參數(shù)里面加入fixture的名稱,默認是手動調(diào)用autouse=False
@pytest.fixture(scope='function') # 需要在用例里傳參
def exe_database_sql():
print("執(zhí)行sql查詢")
yield # 之后的都是后置
print('關(guān)閉數(shù)據(jù)庫連接')
class TestModel1():
def test_case4(self, exe_database_sql): # 加入fixture名稱
print('測試用例4')
3.如果fixtrue通過return或yield返回值,返回值可以通過固件的名字傳遞到測試用例當中,數(shù)據(jù)驅(qū)動
@pytest.fixture(scope='function')
def exe_database_sql():
print("執(zhí)行sql查詢")
return 'success'
class TestModel1():
def test_case4(self, exe_database_sql):
print('測試用例4')
print(exe_database_sql) # 打印success
? class:在類之前和之后執(zhí)行
1.手動調(diào)用的方式:在類的上面加上@pytest.mark.usefixtures(“exe_database_sql”)裝飾器調(diào)用
@pytest.fixture(scope='class', autouse=False)
def exe_database_sql():
print("執(zhí)行sql查詢")
yield # 之后的都是后置
print('關(guān)閉數(shù)據(jù)庫連接')
@pytest.mark.usefixtures("exe_database_sql") # 在類之前和之后調(diào)用
class TestModel1(CommonUtil):
def test_case1(self):
print('測試用例1')
def test_case3(self):
print('測試用例3')
class TestModelA:
def test_case_a1(self):
print('測試a1')
module:模塊之前和之后執(zhí)行package/session:在整個項目會話之前和之后執(zhí)行**
? 1.一般會結(jié)合contest.py文件實現(xiàn)
2.autouse:自動執(zhí)行
自動執(zhí)行,默認是False。
如果希望在另一個py文件中調(diào)用需要結(jié)合contest.py文件使用。
@pytest.fixture(scope='session', autouse=True)
def exe_database_sql():
print("執(zhí)行sql查詢")
yield # 之后的都是后置
print('關(guān)閉數(shù)據(jù)庫連接')
3.params:實現(xiàn)參數(shù)化
? 參數(shù)支持:列表[], 元祖(),字典列表[{},{},{}],字典元祖({},{},{})
? 參數(shù)傳遞到fixture方法:裝飾器使用 params參數(shù)傳參,fixture函數(shù)參數(shù)使用request參數(shù)接收,reques.param取值。通過fixture函數(shù)返回return 參數(shù)。測試用例調(diào)用fixture函數(shù),使用fixture函數(shù)return的參數(shù)。
# 第一步:讀取數(shù)據(jù)方法
def read_yaml():
return ['chenglong', 'zenzidan', 'cai10']
@pytest.fixture(scope='function', autouse=False, params=read_yaml()) # 第二步 參數(shù)params傳遞參數(shù)
def exe_database_sql(request):
print("執(zhí)行sql查詢")
yield request.param # 第三步:固件返回參數(shù)
print('關(guān)閉數(shù)據(jù)庫連接')
class TestModel1:
def test_case1(self):
print('測試模塊1用例1')
def test_case2(self, exe_database_sql): # 第四步:調(diào)用fixture list有幾個值,用例執(zhí)行幾次
print('測試模塊1用例2: '+ exe_database_sql)
4.ids:參數(shù)別名
不能單獨使用,必須和params一起使用,作用是對參數(shù)起別名
# 讀取數(shù)據(jù)方法
def read_yaml():
return ['chenglong', 'zenzidan', 'cai10']
@pytest.fixture(scope='function', autouse=False, params=read_yaml(), ids=['c', 'z', 'cai'])
def exe_database_sql(request):
print("執(zhí)行sql查詢")
yield request.param
print('關(guān)閉數(shù)據(jù)庫連接')
class TestModel1:
def test_case1(self):
print('測試模塊1用例1')
def test_case2(self, exe_database_sql):
print('測試模塊1用例2: '+ exe_database_sql)
5.name: 給fixture起別名
多個fixture的情況下,方便記憶。
特別注意:使用別名后,fixture的名稱就不能再使用,只能使用name。
# 讀取數(shù)據(jù)方法
def read_yaml():
return ['chenglong', 'zenzidan', 'cai10']
@pytest.fixture(scope='function', autouse=False, params=read_yaml(), name='db')
def exe_database_sql(request):
print("執(zhí)行sql查詢")
yield request.param
print('關(guān)閉數(shù)據(jù)庫連接')
class TestModel1:
def test_case1(self):
print('測試模塊1用例1')
def test_case2(self, db):
print('測試模塊1用例2: '+ db) # 調(diào)用時只能使用db
三)fixture結(jié)合conftest.py文件使用
-更多用于前后置-
1.conftest.py是專門用于存放fixture的配置文件。名稱是固定的,不能變;
2.在conftest.py文件里面所有的方法在調(diào)用時都不需要導(dǎo)包;
3.conftest.py文件可以有多個,分層級,并且多個conftest.py文件里面的多個fixture可以被一個用例調(diào)用
如果fixture是function級別,自動調(diào)用,則調(diào)用方式如下:
每個文件夾自動調(diào)用本文件夾下的conftest.py;先調(diào)用父文件夾,再調(diào)用子文件夾。
如果是session級別,自動調(diào)用,則在所有用例執(zhí)行之前和之后執(zhí)行。
四)固件優(yōu)先級
優(yōu)先級從高到低:
fixture session
fixture class
setup_class
fixture function
setup
七、parametrize()實現(xiàn)數(shù)據(jù)驅(qū)動-參數(shù)化
方法:
@pytest.mark.parametrize(args_name, args_value)
args_name: 參數(shù)名稱,用于將參數(shù)值傳遞給函數(shù)。
args_value:參數(shù)值:(列表,字典列表,元祖列表; 元祖,字典元祖),有n個值那么用例執(zhí)行n次。
第一種用法: 使用一個參數(shù)
@pytest.mark.parametrize('caseinfo',['no1','no2','no3']) # 列表
def test_case21(self,caseinfo):
print('測試模塊2用例1 '+caseinfo)
# 執(zhí)行三個用例
# test_index.py::TestLogin::test_case21[no1]
# test_index.py::TestLogin::test_case21[no2]
# test_index.py::TestLogin::test_case21[no3]
# 輸出
# 測試模塊2用例1 no1
# 測試模塊2用例1 no2
# 測試模塊2用例1 no3
@pytest.mark.parametrize('caseinfo', [{'name': '小美', 'age': '18'}, {'name': '小青', 'age': '180'}]) # 字典列表
def test_case22(self, caseinfo):
name = caseinfo['name']
age = caseinfo['age']
print(name, age)
# 執(zhí)行2個用例
# test_index.py::TestLogin::test_case22[caseinfo0]
# test_index.py::TestLogin::test_case22[caseinfo1]
# 輸出
# 小美 18
# 小青 180
第二種用法:使用多個參數(shù)
@pytest.mark.parametrize('arg1, arg2', [('小美', '18'), ('小青', '180')]) # 元祖列表 使用列表嵌套也可以 [['小美', '18'], ['小青', '180']] 看著不好看
def test_case23(self, arg1, arg2):
print(arg1, arg2)
# 執(zhí)行2個用例
# test_index.py::TestLogin::test_case23[小美-18]
# test_index.py::TestLogin::test_case23[小青-180]
# 輸出
# 小美 18
# 小青 180
一) json格式數(shù)據(jù)驅(qū)動
1. json數(shù)據(jù)格式
1.新建json文件,在文件中編寫一個{} 2.有幾個模塊,寫幾個key,值為列表 3.列表中參數(shù)化數(shù)據(jù)有幾組,就寫幾個{} 4.每個{}中組成->說明+參數(shù)數(shù)據(jù)+預(yù)期結(jié)果
{
"login": [
{
"desc": "登錄成功",
"username": "700001",
"password": "700001",
"epxpect": "登錄成功"
},
{
"desc": "登錄失敗",
"username": "700002",
"password": "",
"epxpect": "登錄失敗"
},
{
"desc": "登錄成功",
"username": "700003",
"password": "123456",
"epxpect": "登錄失敗"
}
]
}
注意json文件的格式,gbk 還是utf-8
2. read_json封裝
# read_json.py
import json
class ReadJson:
def __init__(self, json_file):
self.json_file = json_file
def read_json(self, key):
arr = [] # 測試數(shù)據(jù)列表
with open(self.json_file, "r", encoding="utf-8") as f:
for data in json.load(f).get(key):
arr.append(tuple(data.values())[1:]) # 不讀描述
return arr
json_para = ReadJson("test_data.json")
login_para = json_para.read_json("login")
# login_para 得到元祖列表
# [('700001', '700001', '登錄成功'), ('700002', '', '登錄失敗'), ('700003', '123456', '登錄失敗')]
3. 測試用例
import pytest
from read_json import login_para
class TestLogin():
"""登錄測試 """
@pytest.mark.parametrize("username, password, expect_text", login_para) # 前后參數(shù)的順序、數(shù)量必須一致,名字可以不一樣 拆包方法
def test_login(self, username, password, expect_text):
print(f"username:{username}")
print(f"password: {password}")
print(f"expect text: {expect_text}")
"""測試步驟"""
error_info = """獲取提示信息"""
assert expect_text in error_info
二)YAML格式數(shù)據(jù)驅(qū)動
1. yaml數(shù)據(jù)格式
1.新建yaml文件,擴展名可以是yaml或yml 2.支持#注釋 3.通過縮進表示層級,縮進不管空格數(shù)量,對齊就行 4.區(qū)分大小寫 5.支持裸字符,字符串可以用“”也可以不用 6.數(shù)據(jù)組成: map對象 key: value(中間有空格) 數(shù)組(list) 使用 “-” 表示列表 /數(shù)組
舉例:
login: # key 如果是接口用例只有一個key,看封裝的方式,也可以不寫key(模塊名稱)
- case1: 登錄成功 # 用例描述 一個 - 就是一個用例
username: 70001
password: 7001@123
expect: 成功
- case2: 登錄失敗1
username: 70002
password:
expect: 失敗
- case3: 登錄失敗2
username: 70003
password: 70003
expect: 失敗
用途:
做配置文件。環(huán)境變量,數(shù)據(jù)庫信息,用戶名密碼,日志格式。
編寫自動化測試用例,接口測試中常用。
一行的寫法,基本上屬于json格式
msxy: {username: 70001, password: 7001@123}
msxy:[{username: 70001, password: 7001@123},{username: 70002, password: None}]
2. read_yaml封裝方法1: 獲取字典列表
# read_yaml.py
import yaml
class ReadYaml:
"""yaml測試數(shù)據(jù)文件"""
def __init__(self, yaml_file):
self.yaml_file = yaml_file
def read_yaml(self, key):
with open(self.yaml_file, 'r', encoding='utf-8') as f:
data = yaml.load(f, Loader=yaml.FullLoader)
value = data[key] # 如果是接口測試,沒有模塊的區(qū)分,沒有key,直接返回value
return value
def write_yaml(self, data):
with open(self.yaml_file, encoding='utf-8', mode='a') as f:
yaml.dump(data, stream=f, allow_unicode=True)
yaml_para = ReadYaml("test_data.yaml")
login_para = yaml_para.read_yaml('login')
# login_para 為: [{'case1': '登錄成功', 'username': 70001, 'password': '7001@123', 'expect': '成功'}, {'case2': '登錄失敗1', 'username': 70002, 'password': None, 'expect': '失敗'}, {'case3': '登錄失敗2', 'username': 70003, 'password': 70003, 'expect': '失敗'}]
3. 對應(yīng)測試用例1:單參數(shù)
class TestLogin(Common):
"""登錄測試 """
@pytest.mark.parametrize("login_case", login_para)
def test_login(self, login_case):
username = login_case['username']
password = login_case['password']
expect_text = login_case['expect']
print(f"username:{username}")
print(f"password: {password}")
print(f"expect text: {expect_text}")
"""測試步驟"""
error_info = """獲取提示信息"""
assert expect_text in error_info
使用這種封裝,log中的測試用例名稱為函數(shù)名+參數(shù)名+編號,編號從0開始
case:test_index.py_TestLogin_test_login[login_case0]
case:test_index.py_TestLogin_test_login[login_case1]
json也可以使用單參數(shù)的方法,修改read_json封裝方法,返回字典列表。
4. read_yaml封裝2:獲取元祖列表
import yaml
class ReadYaml:
"""yaml測試數(shù)據(jù)文件"""
def __init__(self, yaml_file):
self.yaml_file = yaml_file
def read_yaml(self, key):
with open(self.yaml_file, 'r', encoding='utf-8') as f:
data = yaml.load(f, Loader=yaml.FullLoader)
value = data.[key]
arr = []
for data in value:
arr.append(tuple(data.values())[1:])
return arr
def write_yaml(self, data):
with open(self.yaml_file, encoding='utf-8', mode='a') as f:
yaml.dump(data, stream=f, allow_unicode=True)
yaml_para = ReadYaml("test_data.yaml")
login_para = yaml_para.read_yaml('login')
# login_para 為: [(70001, '7001@123', '成功'), (70002, None, '失敗'), (70003, 70003, '失敗')]
5. 對應(yīng)測試用例2:多參數(shù)
class TestLogin(Common):
"""登錄測試 """
@pytest.mark.parametrize("username, password, expect_text", login_para)
def test_login(self, drivers, username, password, expect_text):
print(f"username:{username}")
print(f"password: {password}")
print(f"expect text: {expect_text}")
"""測試步驟"""
error_info = """獲取提示信息"""
assert expect_text in error_info
使用這種參數(shù)化方法用例名稱顯示為函數(shù)名+參數(shù)值:
case:test_index.py_TestLogin_test_login[70001-7001@mcx-失敗]
三)parametrize中ids 導(dǎo)致編碼亂碼解決
@pytest.mark.parametrize 運行用例導(dǎo)致顯示會形成亂碼,比如
=========================== short test summary info ===========================
FAILED testcases/test_index.py::TestLogin::test_login[70001-70001-\u767b\u5f55\u6210\u529f]
FAILED testcases/test_index.py::TestLogin::test_login[70002-70002-\u767b\u5f55\u6210\u529f]
FAILED testcases/test_index.py::TestLogin::test_login[70003-70003-\u767b\u5f55\u6210\u529f]
有倆種方法解決:
1.第一種:創(chuàng)建個pytest.ini 文件,輸入
[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True
運行就會自動轉(zhuǎn)化為 utf-8的形式。
運行后顯示為:
=========================== short test summary info ===========================
FAILED testcases/test_index.py::TestLogin::test_login[70001-70001-登錄成功]
FAILED testcases/test_index.py::TestLogin::test_login[70002-70002-登錄成功]
FAILED testcases/test_index.py::TestLogin::test_login[70003-70003-登錄成功]
2.第二種:在用例目錄創(chuàng)建 config.py 文件,寫入 (未驗證)
def pytest_collection_modifyitems(items):
for item in items:
item.name = item.name.encode("utf-8").decode("unicode_escape")
item._nodeid = item.nodeid.encode("utf-8").decode("unicode_escape")
會收集每一個用例name和nodeid的中文顯示在控制臺上,轉(zhuǎn)化為utf-8形式。
八、pytest執(zhí)行過程
查詢當前目錄(./)下的conftest.py文件查詢當前目錄下pytest.ini文件,找到測試用例的位置查詢用例目錄下的conftest.py文件查詢測試用例的py文件中是否有setup_class, teardown_class, setup, teardown根據(jù)pytest.ini文件的測試用例的規(guī)則去查找用例并執(zhí)行。
整體過程:
執(zhí)行pytest命令:在命令行中輸入pytest命令,pytest會自動搜尋當前目錄及其子目錄下的測試文件或測試目錄。收集測試文件和測試函數(shù):pytest會根據(jù)配置文件或命令行參數(shù)來收集測試文件和測試函數(shù),收集到的測試函數(shù)被稱為測試用例。執(zhí)行測試用例:pytest會執(zhí)行收集到的所有測試用例,包括測試函數(shù)、測試類中的測試方法等。生成測試報告:pytest會根據(jù)配置文件或命令行參數(shù)來生成測試報告,可以生成多種格式的測試報告,如HTML、XML、JSON等。輸出結(jié)果:pytest會將測試報告輸出到指定的目錄或文件中,也可以在命令行中直接輸出測試結(jié)果,包括測試用例的執(zhí)行情況、測試覆蓋率等信息。清理測試環(huán)境:pytest會在測試用例執(zhí)行完畢后清理測試環(huán)境,包括清理測試數(shù)據(jù)、清理測試用例的狀態(tài)等。
九、pytest識別用例失敗
測試報告展示過程中對失敗用例可能有特殊的要求,比如失敗用例打印,失敗用例截圖等。在pytest框架中可以使用pytest鉤子函數(shù),在測試用例被執(zhí)行后獲取用例的測試結(jié)果,識別每一條用例失敗的情況。
一)pytest鉤子函數(shù)
鉤子函數(shù)是在測試執(zhí)行期間特定事件發(fā)生時被調(diào)用的函數(shù)。任何函數(shù)都可以作為鉤子函數(shù),只需要符合一定的規(guī)則。 鉤子函數(shù)可以具有不同的功能和作用,具體取決于所注冊的鉤子函數(shù)。鉤子函數(shù)可以用于修改測試報告、收集額外的測試數(shù)據(jù)、執(zhí)行額外的操作等。鉤子函數(shù)的作用是允許用戶通過自定義函數(shù)來對測試執(zhí)行過程進行干預(yù)和擴展,以滿足特定的需求。 在 pytest 中,可以使用裝飾器 @pytest.hookimpl 來將函數(shù)注冊為鉤子函數(shù)。注冊就是將函數(shù)與特定的測試執(zhí)行事件關(guān)聯(lián)起來,以便在事件發(fā)生時自動調(diào)用該函數(shù)。 在 pytest 執(zhí)行測試過程中,可以通過注冊 pytest_runtest_makereport 鉤子函數(shù)來輸出測試結(jié)果和生成測試報告。 pytest_runtest_makereport(item, call)` 是 pytest 框架中的一個鉤子函數(shù),用于生成測試報告的每個測試用例的報告。
二)、鉤子函數(shù)的使用方法
步驟:
創(chuàng)建 conftest.py 文件。該文件可以放在測試代碼的根目錄下或者測試目錄的任意層級中。在 conftest.py 文件中,定義一個函數(shù)并將其注冊為 pytest_runtest_makereport 的鉤子函數(shù)。函數(shù)的命名需要遵循 pytest 的命名規(guī)則,一般以 pytest_runtest_makereport 開頭,后面可以再加上自定義的后綴以區(qū)分不同的用途。在鉤子函數(shù)內(nèi)部,可以獲取測試報告的相關(guān)信息,并進行邏輯處理。一般可以通過 result 參數(shù)來獲取測試結(jié)果,通過 report 參數(shù)來獲取測試報告。 示例1:
# conftest.py
def pytest_runtest_makereport(item, call)
# 獲取測試結(jié)果
result= call.excinfo
# 獲取測試報告
report = item.session.config.hook.pytest_runtest_makereport(
item=item, call=call
)
# 處理測試結(jié)果和測試報告
if result.failed or result.skipped:
# 測試失敗或被跳過的情況處理邏輯
pass
else:
# 測試通過的情況處理邏輯
pass
在以上示例中,pytest_runtest_makereport 函數(shù)接受兩個參數(shù):item 和 call。
item表示測試項, call 表示測試的調(diào)用。
通過這兩個參數(shù),可以獲取測試結(jié)果并生成測試報告。 需要注意的是,pytest_runtest_makereport 鉤子函數(shù)在每個測試項運行結(jié)束后都會被調(diào)用一次,因此可以在該函數(shù)內(nèi)部編寫測試結(jié)果和測試報告的邏輯處理代碼。
result = call.excinfo 是在 pytest_runtest_makereport 鉤子函數(shù)中直接獲取測試結(jié)果。call.excinfo 是一個 ExceptionInfo 對象,包含了測試中發(fā)生的異常信息,如果沒有異常則為 None。使用此方法可以直接訪問測試結(jié)果,但不能直接利用 yield 來處理結(jié)果。
三)、 鉤子函數(shù)實現(xiàn)失敗截圖添加到allure
#conftest.py
"""
失敗截圖保存到allure測試報告中
"""
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# 后置,獲取測試結(jié)果
outcome = yield
reps = outcome.get_result()
if reps.when == 'call' and reps.failed:
# 在測試失敗時進行截圖, 添加到allure報告中
img = web_driver.get_screenshot_as_png()
name = '_'.join([reps.nodeid.replace('testcases/', '').replace('::', '_'), dt_strftime('%Y%m%d %H%M%S')]) # 為截圖文件命名
allure.attach(img, name=name, attachment_type=allure.attachment_type.PNG)
在鉤子函數(shù)中:
通過 yield 和 outcome.get_result() 獲取測試用例的結(jié)果。
使用 result.when 來檢查測試結(jié)果是否為 ‘call’(執(zhí)行測試用例的階段)并且是否失敗。
如果測試用例失敗,我們創(chuàng)建了一個對應(yīng)測試用例的截圖,并將其附加到 Allure 報告中。
#conftest.py
"""
失敗截圖保存為文件,同時將文件保存到allure測試報告中
"""
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# 后置,獲取測試結(jié)果
outcome = yield
reps = outcome.get_result()
if reps.when == 'call' and reps.failed:
# 在測試失敗時進行截圖, 添加到allure報告中
file_name = "{}.png".format('_'.join([reps.nodeid.replace('testcases/', '').replace('::', '_'), dt_strftime('%Y%m%d %H%M%S')]))
file_path = os.path.join(cm.dir_img, file_name)
web_driver.get_screenshot_as_file(file_path)
allure.attach.file(file_path, name=file_name, attachment_type=allure.attachment_type.PNG)
文件名的生成是通過替換 result.nodeid 中的雙冒號(‘::’)為下劃線(‘_’)來實現(xiàn)的。 例如,如果當前測試用例的 result.nodeid 為 test_module::test_case,那么截圖文件名將為 test_module_test_case.png。
十、result.nodeid
result.nodeid 是在 pytest 測試框架中表示測試項目的唯一標識符,通常用于標識測試用例或測試集。它是一個字符串類型的屬性。 每個測試項(測試函數(shù)、測試類、測試模塊、測試目錄)都有一個唯一的 nodeid,用于標識該項在整個測試集中的位置。nodeid 由測試項的名稱和其在測試集中的路徑組成。路徑由 :: 分隔符連接,表示從根目錄到當前測試項的層級關(guān)系。 例如,對于以下測試項結(jié)構(gòu):
tests/ └── test_sample.py ├── test_func1 ├── TestClass1 │ ├── test_method1 │ └── test_method2 └── test_func2
result.nodeid 可能會是 test_sample.py::test_func1,表示 test_func1 函數(shù)在 test_sample.py 模塊中的位置。result.nodeid 可能會是 test_sample.py::TestClass1::test_method1,表示 test_method1 方法在 TestClass1 類中的位置。result.nodeid 可能會是 test_sample.py::test_func2,表示 test_func2 函數(shù)在 test_sample.py 模塊中的位置。 在測試執(zhí)行過程中,可以使用 result.nodeid 來唯一標識測試項,并進行進一步的操作和處理,例如生成自定義的測試報告、篩選特定的測試項進行執(zhí)行等。
柚子快報激活碼778899分享:pytest框架介紹
相關(guān)鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。