點(diǎn)此獲取更多相關(guān)資料
本文為霍格沃茲測(cè)試開(kāi)發(fā)學(xué)社學(xué)員學(xué)習(xí)筆記分享
原文鏈接:https://ceshiren.com/t/topic/26755
Pytest 命名規(guī)則
類(lèi)型 | 規(guī)則 |
---|---|
文件 | test_開(kāi)頭 或者 _test 結(jié)尾 |
類(lèi) | Test 開(kāi)頭 |
方法/函數(shù) | test_開(kāi)頭 |
注意:測(cè)試類(lèi)中不可以添加__init__ 構(gòu)造函數(shù) |
注意:pytest對(duì)于測(cè)試包的命名沒(méi)有要求
方法:類(lèi)中定義的函數(shù)
函數(shù):類(lèi)外面定義的函數(shù)
谷歌風(fēng)格開(kāi)源項(xiàng)目風(fēng)格指南:
https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/python_style_rules/ 2
Pytest用例斷言
斷言的概念
斷言(assert),是一種在程序中的一階邏輯(如:一個(gè)結(jié)果為真或假的邏輯判斷式),目的為了表示與驗(yàn)證軟件開(kāi)發(fā)者預(yù)期的結(jié)果。當(dāng)程序執(zhí)行到斷言的位置時(shí),對(duì)應(yīng)的斷言應(yīng)該為真。若斷言不為真時(shí),程序會(huì)中止執(zhí)行,并給出錯(cuò)誤信息。
斷言的用法
-
斷言寫(xiě)法
assert <表達(dá)式>
assert <表達(dá)式>,<描述>
assert <bool expression>; assert <bool expression> : <message>;
示例一
def test_a(): assert True def test_b(): a = 1 b = 1 c = 2 assert a + b == c, f"{a}+{b}=={c}, 結(jié)果為真"
示例二
def test_c(): a = 1 b = 1 c = 2 assert 'abc' in "abcd" import sys def test_plat(): assert ('linux' in sys.platform), "該代碼只能在 Linux 下執(zhí)行"
Pytest測(cè)試框架結(jié)構(gòu)(setup/teardown)
測(cè)試裝置介紹
類(lèi)型 | 規(guī)則 |
---|---|
setup_module/teardown_module | 全局模塊級(jí) |
setup_class/teardown_class | 類(lèi)級(jí),只在類(lèi)中前后運(yùn)行一次 |
setup_function/teardown_function | 函數(shù)級(jí),在類(lèi)外 |
setup_method/teardown_method | 方法級(jí),類(lèi)中的每個(gè)方法執(zhí)行前后 |
setup/teardown | 在類(lèi)中,運(yùn)行在調(diào)用方法的前后(重點(diǎn)) |
Pyrest參數(shù)化用例
參數(shù)化
- 通過(guò)參數(shù)的方式傳遞數(shù)據(jù),從而實(shí)現(xiàn)數(shù)據(jù)和腳本分離。
- 并且可以實(shí)現(xiàn)用例的重復(fù)生成與執(zhí)行。
參數(shù)化測(cè)試函數(shù)使用
單參數(shù)情況
- 單參數(shù),可以將數(shù)據(jù)放在列表中
search_list = ['appium','selenium','pytest']
@pytest.mark.parametrize('name',search_list)
def test_search(name):
assert name in search_list
多參數(shù)情況
- 將數(shù)據(jù)放在列表嵌套元組中
- 將數(shù)據(jù)放在列表嵌套列表中
# 數(shù)據(jù)放在元組中
@pytest.mark.parametrize("test_input,expected",[
("3+5",8),("2+5",7),("7+5",12)
])
def test_mark_more(test_input,expected):
assert eval(test_input) == expected
# 數(shù)據(jù)放在列表中
@pytest.mark.parametrize("test_input,expected",[
["3+5",8],["2+5",7],["7+5",12]
])
def test_mark_more(test_input,expected):
assert eval(test_input) == expected
用例重命名-添加 ids 參數(shù)
- 通過(guò)ids參數(shù),將別名放在列表中
@pytest.mark.parametrize("test_input,expected",[
("3+5",8),("2+5",7),("7+5",12)
],ids=['add_3+5=8','add_2+5=7','add_3+5=12'])
def test_mark_more(test_input,expected):
assert eval(test_input) == expected
用例重命名-添加 ids 參數(shù)(中文)
@pytest.mark.parametrize("test_input,expected",[
("3+5",8),("2+5",7),("7+5",12)
],ids=["3和5相加","2和5相加","7和5相加"])
def test_mark_more(test_input,expected):
assert eval(test_input) == expected
ids不支持中文,默認(rèn)是unicode編碼格式,可以用如下方法轉(zhuǎn)換
# 在項(xiàng)目(最末一級(jí))下創(chuàng)建conftest.py 文件 ,將下面內(nèi)容添加進(jìn)去,運(yùn)行腳本
def pytest_collection_modifyitems(items):
"""
測(cè)試用例收集完成時(shí),將收集到的用例名name和用例標(biāo)識(shí)nodeid的中文信息顯示在控制臺(tái)上
"""
for i in items:
i.name=i.name.encode("utf-8").decode("unicode_escape")
i._nodeid=i.nodeid.encode("utf-8").decode("unicode_escape")
笛卡爾積
接口測(cè)試中用的較多,因?yàn)榻涌诘闹岛芏?/p>
-
兩組數(shù)據(jù)
- a=[1,2,3]
- b=[a,b,c]
-
對(duì)應(yīng)有幾種組合形勢(shì) ?
- (1,a),(1,b),(1,c)
- (2,a),(2,b),(2,c)
- (3,a),(3,b),(3,c)
@pytest.mark.parametrize("b",["a","b","c"])
@pytest.mark.parametrize("a",[1,2,3])
def test_param1(a,b):
print(f"笛卡積形式的參數(shù)化中 a={a} , b={b}")
執(zhí)行方向:由近致遠(yuǎn)
使用 Mark 標(biāo)記測(cè)試用例
Mark:標(biāo)記測(cè)試用例
-
場(chǎng)景:只執(zhí)行符合要求的某一部分用例 可以把一個(gè)web項(xiàng)目劃分多個(gè)模塊,然后指定模塊名稱(chēng)執(zhí)行。
-
解決: 在測(cè)試用例方法上加 @pytest.mark.標(biāo)簽名
-
執(zhí)行: -m 執(zhí)行自定義標(biāo)記的相關(guān)用例
pytest -s 文件名 -m=webtest
pytest -s test_mark_zi_09.py -m apptest
-
pytest -s test_mark_zi_09.py -m "not ios"
(必須要是雙引號(hào)) - pytest -v是能輸出更詳細(xì)的用例信息,成功與失敗不再是.和F
如何解決warnings問(wèn)題
[pytest]
# 在項(xiàng)目目錄下創(chuàng)建一個(gè)pytest.ini放標(biāo)簽,這樣這些標(biāo)簽就不會(huì)warning。而且要換行寫(xiě),也不要頂頭寫(xiě),會(huì)被認(rèn)為是key
# 在這里注冊(cè)好標(biāo)簽名后,pytest可以識(shí)別
markers = str
bignum
float
int
minus
zero
pytest 設(shè)置跳過(guò)、預(yù)期失敗
Mark:跳過(guò)(Skip)及預(yù)期失敗(xFail)
- 這是 pytest 的內(nèi)置標(biāo)簽,可以處理一些特殊的測(cè)試用例,不能成功的測(cè)試用例
- skip - 始終跳過(guò)該測(cè)試用例
- skipif - 遇到特定情況跳過(guò)該測(cè)試用例
- xfail - 遇到特定情況,產(chǎn)生一個(gè)“期望失敗”輸出
Skip 使用場(chǎng)景
-
調(diào)試時(shí)不想運(yùn)行這個(gè)用例
-
標(biāo)記無(wú)法在某些平臺(tái)上運(yùn)行的測(cè)試功能
-
在某些版本中執(zhí)行,其他版本中跳過(guò)
-
比如:當(dāng)前的外部資源不可用時(shí)跳過(guò)
- 如果測(cè)試數(shù)據(jù)是從數(shù)據(jù)庫(kù)中取到的,
- 連接數(shù)據(jù)庫(kù)的功能如果返回結(jié)果未成功就跳過(guò),因?yàn)閳?zhí)行也都報(bào)錯(cuò)
-
解決 1:添加裝飾器
@pytest.mark.skip
@pytest.mark.skipif
-
解決 2:代碼中添加跳過(guò)代碼
pytest.skip(reason)
# 形式一:跳過(guò)這個(gè)方法
@pytest.mark.skip(reason="存在bug")
def test_double_str():
print("代碼未開(kāi)發(fā)完")
assert 'aa' == 'aa'
# 形式二:跳過(guò)這個(gè)方法
@pytest.mark.skipif(sys.platform == "win32", reason="does not run on win32")
def test_case():
print(sys.platform)
assert True
# 形式二:在代碼中跳過(guò)代碼
def check_login():
return False
def test_double_str():
print("start")
if not check_login():
pytest.skip("未登錄,不進(jìn)行下去")
print("end")
# ============================= 1 skipped in 0.02s ======
xfail 使用場(chǎng)景
用于標(biāo)記此用例可能會(huì)失敗,當(dāng)腳本失敗時(shí),測(cè)試報(bào)告也不會(huì)打印錯(cuò)誤追蹤,只是會(huì)顯示xfail狀態(tài)。xfail的主要作用是比如在進(jìn)行測(cè)試提前時(shí),當(dāng)產(chǎn)品某功能尚未開(kāi)發(fā)完成而進(jìn)行自動(dòng)化腳本開(kāi)發(fā),當(dāng)然此時(shí)也可以把這些腳本注釋起來(lái),但這不是pytest推薦的做法,pytest推薦使用xfail標(biāo)記,如此則雖然產(chǎn)品功能尚未開(kāi)發(fā)完成,但是自動(dòng)化腳本已經(jīng)可以跑起來(lái)了,只不過(guò)在測(cè)試報(bào)告中會(huì)顯示xfail而已。
- 與 skip 類(lèi)似 ,預(yù)期結(jié)果為 fail ,標(biāo)記用例為 fail
- 與skip不同,xfail標(biāo)記的用例依然會(huì)被執(zhí)行,如果執(zhí)行成功就返回XPASS,執(zhí)行失敗就返回XFAIL。只是起到一個(gè)提示的作用。
- 用法:添加裝飾器
@pytest.mark.xfail
@pytest.mark.xfail
def test_case():
print("test_xfail 方法執(zhí)行")
assert 2 == 2
# XPASS [100%]test_xfail
@pytest.mark.xfail
def test_case():
print("test_xfail 方法執(zhí)行")
assert 1 == 2
# XFAIL [100%]test_xfail 方法執(zhí)行
xfail = pytest.mark.xfail
@xfail(reason="bug 110")
def test_case():
print("test_xfail 方法執(zhí)行")
assert 1 == 2
# XFAIL (bug 110) [100%]test_xfail 方法執(zhí)行
pytest 運(yùn)行用例
運(yùn)行多條用例方式
如果要進(jìn)入某個(gè)文件所在的目錄終端,可以右鍵文件->選擇open in terminal
-
執(zhí)行包下所有的用例:
pytest/py.test [包名]
-
執(zhí)行單獨(dú)一個(gè) pytest 模塊:
pytest 文件名.py
-
運(yùn)行某個(gè)模塊里面某個(gè)類(lèi):
pytest 文件名.py::類(lèi)名
-
運(yùn)行某個(gè)模塊里面某個(gè)類(lèi)里面的方法:
pytest 文件名.py::類(lèi)名::方法名
-
加-v可以具體展示,如pytest -v 文件名.py::類(lèi)名::方法名 (-v在前在后都行)
- test_skip.py::TestDemo::test_demo1 PASSED [100%]
運(yùn)行結(jié)果分析
- 常用的:fail/error/pass(error可能是代碼寫(xiě)錯(cuò)了)
- 特殊的結(jié)果:warning/deselect(后面會(huì)講)
python 執(zhí)行 pytest
前面已經(jīng)介紹了幾種執(zhí)行用例的方法,一個(gè)是點(diǎn)擊代碼方法或類(lèi)的左側(cè)綠色箭頭,一個(gè)是右鍵測(cè)試用例,一個(gè)是終端pytest解釋器執(zhí)行,我們也可以用python解釋器執(zhí)行
Python 代碼執(zhí)行 pytest
-
方法一:使用 main 函數(shù)
-
方法二:使用 python -m pytest 調(diào)用 pytest(jenkins 持續(xù)集成用到),相當(dāng)于在原來(lái)pytest 用例前加了python -m。
- 方便指定python版本,比如有的用例使用python老版本寫(xiě)的
Python 代碼執(zhí)行 pytest - main 函數(shù)
if __name__ == '__main__':
# 1、運(yùn)行當(dāng)前目錄下所有符合規(guī)則的用例,包括子目錄(test_*.py 和 *_test.py)
pytest.main()
# 2、運(yùn)行test_mark1.py::test_dkej模塊中的某一條用例
pytest.main(['test_mark1.py::test_dkej','-vs'])
# 3、運(yùn)行某個(gè) 標(biāo)簽
pytest.main(['test_mark1.py','-vs','-m','dkej'])
運(yùn)行方式
`python test_*.py `
pytest 異常處理
異常處理方法 try …except
try:
可能產(chǎn)生異常的代碼塊
except [ (Error1, Error2, ... ) [as e] ]:
處理異常的代碼塊1
except [ (Error3, Error4, ... ) [as e] ]:
處理異常的代碼塊2
except [Exception]:
處理其它異常
異常處理方法 pytest.raise()
- 可以捕獲特定的異常
- 獲取捕獲的異常的細(xì)節(jié)(異常類(lèi)型,異常信息)
- 發(fā)生異常,后面的代碼將不會(huì)被執(zhí)行
def test_raise():
with pytest.raises(ValueError, match='must be 0 or None'):
raise ValueError("value must be 0 or None")
def test_raise1():
with pytest.raises(ValueError) as exc_info:
raise ValueError("value must be 42")
assert exc_info.type is ValueError
assert exc_info.value.args[0] == "value must be 42"
這樣可以選擇一個(gè)異常
def test_raise():
with pytest.raises((ZeroDivisionError,ValueError)):
raise ZeroDivisionError("value must be 0 or None")
Pytest 結(jié)合數(shù)據(jù)驅(qū)動(dòng) YAML
數(shù)據(jù)驅(qū)動(dòng)
-
什么是數(shù)據(jù)驅(qū)動(dòng)?
- 數(shù)據(jù)驅(qū)動(dòng)就是數(shù)據(jù)的改變從而驅(qū)動(dòng)自動(dòng)化測(cè)試的執(zhí)行,最終引起測(cè)試結(jié)果的改變。簡(jiǎn)單來(lái)說(shuō),就是參數(shù)化的應(yīng)用。數(shù)據(jù)量小的測(cè)試用例可以使用代碼的參數(shù)化來(lái)實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng),數(shù)據(jù)量大的情況下建議大家使用一種結(jié)構(gòu)化的文件(例如 yaml,json 等)來(lái)對(duì)數(shù)據(jù)進(jìn)行存儲(chǔ),然后在測(cè)試用例中讀取這些數(shù)據(jù)。
-
應(yīng)用:
- App、Web、接口自動(dòng)化測(cè)試
- 測(cè)試步驟的數(shù)據(jù)驅(qū)動(dòng)
- 測(cè)試數(shù)據(jù)的數(shù)據(jù)驅(qū)動(dòng)
- 配置的數(shù)據(jù)驅(qū)動(dòng)
yaml 文件介紹
-
對(duì)象:鍵值對(duì)的集合,用冒號(hào) “:” 表示
-
數(shù)組:一組按次序排列的值,前加 “-”
-
純量:?jiǎn)蝹€(gè)的、不可再分的值
- 字符串
- 布爾值
- 整數(shù)
- 浮點(diǎn)數(shù)
- Null
- 時(shí)間
- 日期
# 編程語(yǔ)言
languages:
- PHP
- Java
- Python
book:
Python入門(mén): # 書(shū)籍名稱(chēng)
price: 55.5
author: Lily
available: True
repertory: 20
date: 2018-02-17
Java入門(mén):
price: 60
author: Lily
available: False
repertory: Null
date: 2018-05-11
相當(dāng)于:
languages:['PHP', 'Java', 'Python'] # languages是key值
yaml 文件使用
-
查看 yaml 文件
- pycharm
- txt 記事本
-
讀取 yaml 文件
- 安裝:
pip install pyyaml
- 方法:
yaml.safe_load(f)
(yaml->python) - 方法:
yaml.safe_dump(f)
(python->yaml)
- 安裝:
import yaml
file_path = './my.yaml'
with open(file_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
工程目錄結(jié)構(gòu)(看項(xiàng)目文件datadriver_yaml)
- data 目錄:存放 yaml 數(shù)據(jù)文件
- func 目錄:存放被測(cè)函數(shù)文件
- testcase 目錄:存放測(cè)試用例文件
# 工程目錄結(jié)構(gòu)
.
├── data
│ └── data.yaml
├── func
│ ├── __init__.py
│ └── operation.py
└── testcase
├── __init__.py
└── test_add.py
測(cè)試準(zhǔn)備
- 被測(cè)對(duì)象:
operation.py
- 測(cè)試用例:
test_add.py
- 測(cè)試數(shù)據(jù):
data.yaml
# operation.py 文件內(nèi)容
def my_add(x, y):
result = x + y
return result
# test_add.py 文件內(nèi)容
class TestWithYAML:
@pytest.mark.parametrize('x,y,expected', [[1, 1, 2]])
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
# data.yaml 文件內(nèi)容
-
- 1
- 1
- 2
-
- 3
- 6
- 9
-
- 100
- 200
- 300
Pytest 數(shù)據(jù)驅(qū)動(dòng)結(jié)合 yaml 文件
# 讀取yaml文件
def get_yaml():
"""
獲取json數(shù)據(jù)
:return: 返回?cái)?shù)據(jù)的結(jié)構(gòu):[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
"""
with open('../datas/data.yaml', 'r') as f:
data = yaml.safe_load(f)
return data
Pytest 結(jié)合數(shù)據(jù)驅(qū)動(dòng) Excel
讀取 Excel 文件
-
第三方庫(kù)
xlrd
xlwings
pandas
-
openpyxl
- 官方文檔: openpyxl - A Python library to read/write Excel 2010 xlsx/xlsm files — openpyxl 3.1.2 documentation
openpyxl 庫(kù)的安裝
- 安裝:
pip install openpyxl
- 導(dǎo)入:
import openpyxl
openpyxl 庫(kù)的操作
- 讀取工作簿
- 讀取工作表
- 讀取單元格
import openpyxl
# 獲取工作簿
book = openpyxl.load_workbook('../data/params.xlsx')
# 讀取工作表
sheet = book.active
# 讀取單個(gè)單元格
cell_a1 = sheet['A1']
cell_a3 = sheet.cell(column=1, row=3) # A3
# 讀取多個(gè)連續(xù)單元格
cells = sheet["A1":"C3"]
# 獲取單元格的值
cell_a1.value
工程目錄結(jié)構(gòu)
- data 目錄:存放 excel 數(shù)據(jù)文件
- func 目錄:存放被測(cè)函數(shù)文件
- testcase 目錄:存放測(cè)試用例文件
# 工程目錄結(jié)構(gòu)
.
├── data
│ └── params.excel
├── func
│ ├── __init__.py
│ └── operation.py
└── testcase
├── __init__.py
└── test_add.py
測(cè)試準(zhǔn)備
- 被測(cè)對(duì)象:
operation.py
- 測(cè)試用例:
test_add.py
# operation.py 文件內(nèi)容
def my_add(x, y):
result = x + y
return result
# test_add.py 文件內(nèi)容
class TestWithEXCEL:
@pytest.mark.parametrize('x,y,expected', get_excel())
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
測(cè)試準(zhǔn)備
- 測(cè)試數(shù)據(jù):
params.xlsx
注意:.xlsx文件要在外面創(chuàng)建,不要在編輯器里創(chuàng)建
Pytest 數(shù)據(jù)驅(qū)動(dòng)結(jié)合 Excel 文件
# 讀取Excel文件
import openpyxl
import pytest
def get_excel():
# 獲取工作簿
book = openpyxl.load_workbook('../data/params.xlsx')
# 獲取活動(dòng)行(非空白的)
sheet = book.active
# 提取數(shù)據(jù),格式:[[1, 2, 3], [3, 6, 9], [100, 200, 300]]
values = []
for row in sheet:
line = []
for cell in row:
line.append(cell.value)
values.append(line)
return values
Pytest 結(jié)合數(shù)據(jù)驅(qū)動(dòng) csv
csv 文件介紹
- csv:逗號(hào)分隔值
- 是 Comma-Separated Values 的縮寫(xiě)
- 以純文本形式存儲(chǔ)數(shù)字和文本
- 文件由任意數(shù)目的記錄組成
- 每行記錄由多個(gè)字段組成
Linux從入門(mén)到高級(jí),linux,¥5000
web自動(dòng)化測(cè)試進(jìn)階,python,¥3000
app自動(dòng)化測(cè)試進(jìn)階,python,¥6000
Docker容器化技術(shù),linux,¥5000
測(cè)試平臺(tái)開(kāi)發(fā)與實(shí)戰(zhàn),python,¥8000
csv 文件使用
-
讀取數(shù)據(jù)
- 內(nèi)置函數(shù):
open()
- 內(nèi)置模塊:
csv
- 內(nèi)置函數(shù):
-
方法:
csv.reader(iterable)
- 參數(shù):iterable ,文件或列表對(duì)象
- 返回:迭代器,每次迭代會(huì)返回一行數(shù)據(jù)。
# 讀取csv文件內(nèi)容
def get_csv():
with open('demo.csv', 'r') as file:
raw = csv.reader(file)
for line in raw:
print(line)
工程目錄結(jié)構(gòu)
- data 目錄:存放 csv 數(shù)據(jù)文件
- func 目錄:存放被測(cè)函數(shù)文件
- testcase 目錄:存放測(cè)試用例文件
# 工程目錄結(jié)構(gòu)
.
├── data
│ └── params.csv
├── func
│ ├── __init__.py
│ └── operation.py
└── testcase
├── __init__.py
└── test_add.py
測(cè)試準(zhǔn)備
- 被測(cè)對(duì)象:
operation.py
- 測(cè)試用例:
test_add.py
- 測(cè)試數(shù)據(jù):
params.csv
# operation.py 文件內(nèi)容
def my_add(x, y):
result = x + y
return result
# test_add.py 文件內(nèi)容
class TestWithCSV:
@pytest.mark.parametrize('x,y,expected', [[1, 1, 2]])
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
# params.csv 文件內(nèi)容
1,1,2
3,6,9
100,200,300
Pytest 數(shù)據(jù)驅(qū)動(dòng)結(jié)合 csv 文件
# 讀取 data目錄下的 params.csv 文件
import csv
def get_csv():
"""
獲取csv數(shù)據(jù)
:return: 返回?cái)?shù)據(jù)的結(jié)構(gòu):[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
"""
with open('../data/params.csv', 'r') as file:
raw = csv.reader(file)
data = []
for line in raw:
data.append(line)
return data
Pytest 結(jié)合數(shù)據(jù)驅(qū)動(dòng) json
json 文件介紹
-
json 是 JS 對(duì)象
-
全稱(chēng)是 JavaScript Object Notation
-
是一種輕量級(jí)的數(shù)據(jù)交換格式
-
json 結(jié)構(gòu)
- 對(duì)象
{"key": value}
- 數(shù)組
[value1, value2 ...]
- 對(duì)象
{
"name:": "hogwarts ",
"detail": {
"course": "python",
"city": "北京"
},
"remark": [1000, 666, 888]
}
json 文件使用
-
查看 json 文件
- pycharm
- txt 記事本
-
讀取 json 文件
- 內(nèi)置函數(shù) open()
- 內(nèi)置庫(kù) json
- 方法:
json.loads()
- 方法:
json.dumps()
# 讀取json文件內(nèi)容
def get_json():
with open('demo.json', 'r') as f:
data = json.loads(f.read())
print(data)
測(cè)試準(zhǔn)備
- 被測(cè)對(duì)象:
operation.py
- 測(cè)試用例:
test_add.py
- 測(cè)試數(shù)據(jù):
params.json
# operation.py 文件內(nèi)容
def my_add(x, y):
result = x + y
return result
# test_add.py 文件內(nèi)容
class TestWithJSON:
@pytest.mark.parametrize('x,y,expected', [[1, 1, 2]])
def test_add(self, x, y, expected):
assert my_add(int(x), int(y)) == int(expected)
# params.json 文件內(nèi)容
{
"case1": [1, 1, 2],
"case2": [3, 6, 9],
"case3": [100, 200, 300]
}
Pytest 數(shù)據(jù)驅(qū)動(dòng)結(jié)合 json 文件
# 讀取json文件
def get_json():
"""
獲取json數(shù)據(jù)
:return: 返回?cái)?shù)據(jù)的結(jié)構(gòu):[[1, 1, 2], [3, 6, 9], [100, 200, 300]]
"""
with open('../data/params.json', 'r') as f:
data = json.loads(f.read())
return list(data.values())
pytest測(cè)試用例生命周期管理
Fixture 用法
Fixture 特點(diǎn)及優(yōu)勢(shì)
- 1?命令靈活:對(duì)于 setup,teardown,可以不起這兩個(gè)名字
- 2?數(shù)據(jù)共享:在 conftest.py 配置?寫(xiě)?法可以實(shí)現(xiàn)數(shù)據(jù)共享,不需要 import 導(dǎo)???梢钥?件共享
- 3?scope 的層次及神奇的 yield 組合相當(dāng)于各種 setup 和 teardown
- 4、實(shí)現(xiàn)參數(shù)化
Fixture 在自動(dòng)化中的應(yīng)用- 基本用法
-
場(chǎng)景:
測(cè)試?例執(zhí)?時(shí),有的?例需要登陸才能執(zhí)?,有些?例不需要登陸。
setup 和 teardown ?法滿(mǎn)?。fixture 可以。默認(rèn) scope(范圍)function
-
步驟:
- 1.導(dǎo)? pytest
- 2.在登陸的函數(shù)上?加@pytest.fixture()
- 3.在要使?的測(cè)試?法中傳?(登陸函數(shù)名稱(chēng)),就先登陸
- 4.不傳?的就不登陸直接執(zhí)?測(cè)試?法。
# test_fixture.py
import pytest
# 定義登錄的fixture
@pytest.fixture()
def login():
print("完成登錄操作")
def test_search():
print("搜索")
def test_cart(login): #不需要把login放在函數(shù)里面,只要傳參就可以
print("購(gòu)物車(chē)")
def test_order(login):
print("下單")
Fixture 在自動(dòng)化中的應(yīng)用 - 作用域
取值 | 范圍 | 說(shuō)明 |
---|---|---|
function | 函數(shù)級(jí) | 每一個(gè)函數(shù)或方法都會(huì)調(diào)用 |
class | 類(lèi)級(jí)別 | 每個(gè)測(cè)試類(lèi)只運(yùn)行一次 |
module | 模塊級(jí) | 每一個(gè).py 文件調(diào)用一次 |
package | 包級(jí) | 每一個(gè) python 包只調(diào)用一次(暫不支持) |
session | 會(huì)話(huà)級(jí) | 每次會(huì)話(huà)只需要運(yùn)行一次,會(huì)話(huà)內(nèi)所有方法及類(lèi),模塊都共享這個(gè)方法 |
注:整個(gè)項(xiàng)目就是一個(gè)會(huì)話(huà)
import pytest
# 定義登錄的fixture
@pytest.fixture(scope="class")
def login():
print("完成登錄操作")
def test_search(login):
print("搜索")
def test_cart(login):
print("購(gòu)物車(chē)")
def test_order(login):
print("下單")
class TestDemo:
def test_case1(self,login):
print("case1")
def test_case2(self,login):
print("case2")
Fixture 在自動(dòng)化中的應(yīng)用 - yield 關(guān)鍵字
- 場(chǎng)景:
你已經(jīng)可以將測(cè)試?法【前要執(zhí)?的或依賴(lài)的】解決了,
測(cè)試?法后銷(xiāo)毀清除數(shù)據(jù)的要如何進(jìn)?呢?
- 解決:
通過(guò)在 fixture 函數(shù)中加? yield 關(guān)鍵字,yield 是調(diào)?第?次返回結(jié)果,
第?次執(zhí)?它下?的語(yǔ)句返回。
- 步驟:
在@pytest.fixture(scope=module)。
在登陸的?法中加 yield,之后加銷(xiāo)毀清除的步驟
import pytest
# 定義登錄的fixture
@pytest.fixture(scope="class")
def login():
# setup 操作
print("完成登錄操作")
token = "abcd"
username = "hogwarts"
yield token, username # 相當(dāng)于return
# teardown 操作
print("完成登出操作")
def test_search(login):
token, username = login
print(f"token:{token},name:{username}")
print("搜索")
def test_cart(login):
print("購(gòu)物車(chē)")
def test_order(login):
print("下單")
class TestDemo:
def test_case1(self,login):
print("case1")
def test_case2(self,login):
print("case2")
Fixture 在自動(dòng)化中的應(yīng)用 - 數(shù)據(jù)共享
- 場(chǎng)景:
你與其他測(cè)試?程師合作?起開(kāi)發(fā)時(shí),公共的模塊要在不同?件中,要在?家都訪(fǎng)問(wèn)到的地?。
- 解決:
使? conftest.py 這個(gè)?件進(jìn)?數(shù)據(jù)共享,并且他可以放在不同位置起著不同的范圍共享作?。
-
前提:
- conftest ?件名是不能換的
- 放在項(xiàng)?下是全局的數(shù)據(jù)共享的地?
-
執(zhí)?:
- 系統(tǒng)執(zhí)?到參數(shù) login 時(shí)先從本模塊中查找是否有這個(gè)名字的變量什么的,
- 之后在 conftest.py 中找是否有。
-
步驟:
- 將登陸模塊帶@pytest.fixture 寫(xiě)在 conftest.py
Fixture 在自動(dòng)化中的應(yīng)用 - 自動(dòng)應(yīng)用
場(chǎng)景:
不想原測(cè)試?法有任何改動(dòng),或全部都?動(dòng)實(shí)現(xiàn)?動(dòng)應(yīng)?,
沒(méi)特例,也都不需要返回值時(shí)可以選擇?動(dòng)應(yīng)?
解決:
使? fixture 中參數(shù) autouse=True 實(shí)現(xiàn)
步驟:
在?法上?加 @pytest.fixture(autouse=True)
比如要實(shí)現(xiàn)fixture時(shí)session級(jí)別的,就要每個(gè)用例都添加fixture方法。可以通過(guò)自動(dòng)應(yīng)用來(lái)避免。
問(wèn)題:那yield返回參數(shù)的怎么辦?
Fixture 在自動(dòng)化中的應(yīng)用 -參數(shù)化
場(chǎng)景:
測(cè)試離不開(kāi)數(shù)據(jù),為了數(shù)據(jù)靈活,?般數(shù)據(jù)都是通過(guò)參數(shù)傳的
解決:
fixture 通過(guò)固定參數(shù) request 傳遞
步驟:
在 fixture 中增加@pytest.fixture(params=[1, 2, 3, ‘linda’])文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-687936.html
在?法參數(shù)寫(xiě) request,方法體里面使用 request.param 接收參數(shù)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-687936.html
Fixture 的用法總結(jié)
- 模擬 setup,teardown(一個(gè)用例可以引用多個(gè) fixture)
- yield 的用法
- 作用域( session,module, 類(lèi)級(jí)別,方法級(jí)別 )
- 自動(dòng)執(zhí)行 (autouse 參數(shù))
- conftest.py 用法,一般會(huì)把 fixture 寫(xiě)在 conftest.py 文件中(這個(gè)文件名字是固定的,不能改)
- 實(shí)現(xiàn)參數(shù)化
# test_fixture_param.py
import pytest
@pytest.fixture(params=[["selenium",123], ["appium",123]])
def login(request):
print(f"用戶(hù)名:{request.param}")
return request.param
def test_demo1(login):
print(f"demo1 case:數(shù)據(jù)為{login}")
到了這里,關(guān)于軟件測(cè)試/測(cè)試開(kāi)發(fā)丨Pytest和Allure報(bào)告 學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!