- 導(dǎo)包 import unittest
- 測試類以Test開頭
- 測試方法名稱必須以test開頭
pytest命令行運行
pytest test_case.py
顯示詳細信息
pytest -v test_case.py
pytest -v --return 2 --html=./report/demo_test_report.html test_case.py
Pytest的setup和teardown函數(shù)
1.setup和teardown主要分為:模塊級,類級,功能級,函數(shù)級。
2.存在于測試類內(nèi)部
import pytest
class TestABC(object):
# 測試類級開始
def setup_class(self):
print("------->setup_class")
# 測試類級結(jié)束
def teardown_class(self):
print("------->teardown_class")
# 函數(shù)級開始
def setup(self):
print("------->setup_method")
# 函數(shù)級結(jié)束
def teardown(self):
print("------->teardown_method")
def test_a(self):
print("------->test_a")
assert 1
def test_b(self):
print("------->test_b")
assert 0
if __name__ == '__main__':
pytest.main()
Pytest配置文件
pytest的配置文件通常放在測試目錄下,名稱為pytest.ini,命令行運行時會使用該配置文件中的配置.
文件第一行 [pytest]
- 配置pytest命令行運行參數(shù)
詳細信息輸出 -v
addopts = -v
空格分隔,可添加多個命令行參數(shù) -所有參數(shù)均為插件包的參數(shù)
例如:addopts = -v --return 2 --html=./report/report.html - 配置測試搜索的路徑
當前目錄下的scripts文件夾 -可自定義
testpaths = ./scripts - 配置測試搜索的文件名
以test_開頭,以.py結(jié)尾的所有文件 -可自定義
python_files = test_*.py
-配置測試搜索的測試類名
以Test_開頭的類 -可自定義
python_classes = Test_* - 配置測試搜索的測試函數(shù)名
以test_開頭的方法 -可自定義
python_functions = test_*
Pytest常用插件
插件列表:https://plugincompat.herokuapp.com
html測試報告
插件名稱:pytest-html
命令行格式:
pytest --html=用戶路徑/report.html
pytest --html=用戶路徑/report.xml
例如:pytest -s --html=./report.html test_abc.pyPytest控制函數(shù)執(zhí)行順序
函數(shù)修飾符的方式標記被測試函數(shù)執(zhí)行的順序,默認情況下,pytest是根據(jù)測試方法名由小到大執(zhí)行的,可以通過第三方插件包改變其運行順序。
插件名稱:pytest-ordering
@pytest.mark.run(order=x)修飾函數(shù)
@pytest.mark.run(order=2)
def test_a(self):
print('\n-----------a')
assert 1
@pytest.mark.run(order=1)
def test_b(self):
print('\n------------b')
assert 0
"""
test_abc.py
------------b
F
-----------a
.
"""
- Pytest失敗重試
通過命令行方式,控制失敗函數(shù)的重試次數(shù)
插件名稱:pytest-rerunfailures
命令行格式:pytest --reruns n
n:為重試的次數(shù)
例如:pytest -s --return 2 --html=./report.html test_abc.py
fixture
fixture修飾器來標記固定的工廠函數(shù),在其他函數(shù),模塊,類或整個工程調(diào)用它時會被激活并優(yōu)先執(zhí)行,通常會被用于完成預(yù)置處理和重復(fù)操作。
比如測試用例的前置條件 的方法,可以用fixture修飾,在需要該前置條件的測試方法前調(diào)用。
方法:fixture(scope="function", params=None, autouse=False, ids=None, name=None)
常用參數(shù):
scope:被標記方法的作用域
??function" (default):作用于每個測試方法,每個test都運行一次
??"class":作用于整個類,每個class的所有test只運行一次
??"module":作用于整個模塊,每個module的所有test只運行一次
??"session:作用于整個session(慎用),每個session只運行一次
params:(list類型)提供參數(shù)數(shù)據(jù),供調(diào)用標記方法的函數(shù)使用
autouse:是否自動運行,默認為False不運行,設(shè)置為True自動運行
import pytest
# --------------------------- 通過參數(shù)引用 函數(shù)級別 傳入before的測試方法執(zhí)行前會執(zhí)行before------------------------------
class TestAAA(object):
@pytest.fixture()
def before(self):
print("------->before")
def test_a1(self, before):
print("------->test_a1")
assert 1
# 一般用這種方式
@pytest.mark.usefixtures("before")
def test_a2(self):
print("------->test_a2")
assert 1
# --------------------------- 通過函數(shù)引用 函數(shù)級別 TestBBB類中每個測試方法執(zhí)行前都會執(zhí)行before_outter_class------------------------------
@pytest.fixture()
def before_outter_class():
print("------->outter class before")
@pytest.mark.usefixtures("before_outter_class")
class TestBBB(object):
def setup(self):
print('-------classBBB setup')
def test_b1(self):
print("------->test_b1")
assert 1
def test_b2(self):
print("------->test_b2")
assert 1
# --------------------------- 設(shè)置為默認運行 函數(shù)級別 每個測試方法執(zhí)行前都會自動執(zhí)行------------------------------
@pytest.fixture(autouse=True)
def before_outter_class_c():
print("------->outter class before ccccc")
class TestCCC(object):
def setup(self):
print('-------classCCC setup')
def test_c(self):
print("------->test_c")
assert 1
# --------------------------- 設(shè)置作用域 默認是函數(shù)級別------------------------------
# class級別 TestDDD類中測試方法執(zhí)行前執(zhí)行一次before_outter_class_d()
@pytest.fixture(scope='class', autouse=True)
def before_outter_class_d():
print("------->outter class before ddddd")
class TestDDD(object):
def setup(self):
print('-------classDDDD setup')
def test_d1(self):
print("------->test_d1")
assert 1
def test_d2(self):
print("------->test_d2")
assert 1
# --------------------------- 返回值------------------------------
@pytest.fixture(params=[1, 2, 3])
def need_data(request):
return request.param
class TestABC(object):
def test_abc(self, need_data):
print("------->test_abc")
# 拿到返回值做一次斷言 會執(zhí)行3次
assert need_data != 2
# 注意:@pytest.mark.usefixtures("need_data") 這種方式拿不到返回值
@pytest.mark.usefixtures("need_data")
def test_abc222(self):
print("------->test_abc222")
assert False
跳過測試函數(shù)
方法:
skipif(condition, reason=None)
參數(shù):
condition:跳過的條件,必傳參數(shù)
reason:標注原因,必傳參數(shù)
使用方法:
@pytest.mark.skipif(condition, reason="xxx")
# 跳過測試函數(shù)test_b
@pytest.mark.skipif(condition=True, reason="就是不想執(zhí)行")
def test_b(self):
print("------->test_b")
assert 0
標記為預(yù)期失敗函數(shù)
方法:
xfail(condition=None, reason=None, raises=None, run=True, strict=False)
常用參數(shù):
condition:預(yù)期失敗的條件,必傳參數(shù)
reason:失敗的原因,必傳參數(shù)
使用方法:
@pytest.mark.xfail(condition, reason="xx")
# 標記為預(yù)期失敗函數(shù)test_c
@pytest.mark.xfail(condition=2 > 1, reason="標注為預(yù)期失敗")
def test_c(self):
print("------->test_c")
assert 0
函數(shù)數(shù)據(jù)參數(shù)化
方法:
parametrize(argnames, argvalues, indirect=False, ids=None, scope=None)
常用參數(shù):
argnames:參數(shù)名
argvalues:參數(shù)對應(yīng)值,類型必須為list
當參數(shù)為一個值時格式:[value]
當參數(shù)個數(shù)大于一個時,格式為:[(param_value1,param_value2.....),(param_value1,param_value2.....)]
使用方法:
@pytest.mark.parametrize(argnames,argvalues)
?? 參數(shù)值為N個,測試方法就會運行N次
# --------------------------- 單個參數(shù) ---------------------------------
class TestAAA(object):
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
@pytest.mark.parametrize("a", [3, 6]) # a參數(shù)被賦予兩個值,函數(shù)會運行兩遍
def test_a(self, a): # 參數(shù)必須和parametrize里面的參數(shù)一致
print("test data:a=%d" % a)
assert a % 3 == 0
# --------------------------- 多個參數(shù) ---------------------------------
def get_test_data():
return [(1, 2), (0, 3)]
class TestBBB:
def setup_class(self):
print("------->setup_class bbb")
def teardown_class(self):
print("------->teardown_class bbb")
# 參數(shù)a,b均被賦予兩組值,函數(shù)會運行兩遍
# @pytest.mark.parametrize("a,b", [(1, 2), (0, 3)])
@pytest.mark.parametrize("a,b", get_test_data())
# 參數(shù)必須和parametrize里面的參數(shù)一致
def test_b(self, a, b):
print("test data:a=%d,b=%d" % (a, b))
assert a + b == 3
數(shù)據(jù)驅(qū)動
- yaml
- yaml文件格式
語法規(guī)則
1.大小寫敏感
2.使用縮進表示層級關(guān)系
3.縮進時不允許使用Tab鍵,只允許使用空格。
4.縮進的空格數(shù)目不重要,只要相同層級的元素左側(cè)對齊即可
- 讀取yaml文件
yaml.load(stream, Loader=Loader)
with open("../resouce/data.yaml", 'r') as f:
data = yaml.load(f, Loader=yaml.FullLoader)
print(type(data))
print(data)
- 寫yaml文件
yaml.dump(data,stream,**kwds)
常用參數(shù):
data:寫入數(shù)據(jù)類型為字典
stream:打開文件對象
encoding='utf-8' # 設(shè)置寫入編碼格式
allow_unicode=True # 是否允許unicode編碼
data = {'Search_Data': {
'search_test_003': {'expect': {'value': '不好了'}, 'value': '著火了'},
'search_test_004': {'expect': [8, 8, 6], 'value': 666}}}
with open("../resouce/data.yaml", "w") as f:
yaml.dump(data, f, encoding='utf-8', allow_unicode=True)
- 錨點&和引用*
錨點:標注一個內(nèi)容,錨點名稱自定義
引用:使用被標注的內(nèi)容<<: *錨點名
data.yaml
data: &imp
value: 456
name:
value1: 123
<<: *imp # "<<:" 合并到當前位置,"*imp" 引用錨點imp
轉(zhuǎn)換為python代碼
{'data': {'value': 456}, 'name': {'value': 456, 'value1': 123}}
生成測試報告
- Allure
Allure是一個獨立的報告插件,生成美觀易讀的報告,目前支持語言:Java, PHP, Ruby, Python, Scala, C#。
幫助文檔:https://docs.qameta.io/allure/#_about
生成Allure報告
pytest --alluredir report# 在執(zhí)行命令目錄生成report文件夾,文件夾下包含xml文件 - xml轉(zhuǎn)html工具安裝
- Mac版本
(1)brew install allure
(2)進入report上級目錄執(zhí)行命令:allure generate report/ -o report/html
(3)report目錄下會生成index.html文件,即為可視化報告 - windows版本
(1)下載壓縮包allure-2.5.0.zip 解壓
(2)將壓縮包內(nèi)的bin目錄配置到path系統(tǒng)環(huán)境變量
(3)進入report上級目錄執(zhí)行命令:allure generate report/ -o report/html
(4)report目錄下會生成index.html文件,即為可視化報告
添加測試步驟
@allure.step(title="測試步驟001")
添加測試描述
allure.attach('描述', '我是測試步驟001的描述~~~')
添加嚴重級別
@pytest.allure.severity(Severity)
參數(shù) Severity:嚴重級別(BLOCKER,CRITICAL,NORMAL,MINOR,TRIVIAL)
使用方式:@pytest.allure.severity(pytest.allure.severity_level.CRITICAL)
test_all.py
import allure, pytest
class Test_allure:
def setup(self):
pass
def teardown(self):
pass
@pytest.mark.parametrize("a",[1,2,3])
@pytest.allure.severity(pytest.allure.severity_level.CRITICAL)
@allure.step('我是測試步驟001')
def test_al(self, a):
allure.attach('描述', '我是測試步驟001的描述~~~')
assert a != 2

