- 导包 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函数
1.setup和teardown主要分为:模块级,类级,功能级,函数级。
2.存在于测试类内部
import pytest
class TestABC(object):
# 测试类级开始
def setup_class(self):
print("------->setup_class")
# 测试类级结束
def teardown_class(self):
print("------->teardown_class")
# 函数级开始
def setup(self):
print("------->setup_method")
# 函数级结束
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命令行运行参数
详细信息输出 -v
addopts = -v
空格分隔,可添加多个命令行参数 -所有参数均为插件包的参数
例如:addopts = -v --return 2 --html=./report/report.html
- 配置测试搜索的路径
当前目录下的scripts文件夹 -可自定义
testpaths = ./scripts
- 配置测试搜索的文件名
以test_开头,以.py结尾的所有文件 -可自定义
python_files = test_*.py
-配置测试搜索的测试类名
以Test_开头的类 -可自定义
python_classes = Test_*
- 配置测试搜索的测试函数名
以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.py
Pytest控制函数执行顺序
函数修饰符的方式标记被测试函数执行的顺序,默认情况下,pytest是根据测试方法名由小到大执行的,可以通过第三方插件包改变其运行顺序。
插件名称:pytest-ordering
@pytest.mark.run(order=x)修饰函数
@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失败重试
通过命令行方式,控制失败函数的重试次数
插件名称:pytest-rerunfailures
命令行格式:pytest --reruns n
n:为重试的次数
例如:pytest -s --return 2 --html=./report.html test_abc.py
fixture
fixture修饰器来标记固定的工厂函数,在其他函数,模块,类或整个工程调用它时会被激活并优先执行,通常会被用于完成预置处理和重复操作。
比如测试用例的前置条件 的方法,可以用fixture修饰,在需要该前置条件的测试方法前调用。
方法:fixture(scope="function", params=None, autouse=False, ids=None, name=None)
常用参数:
scope:被标记方法的作用域
function" (default):作用于每个测试方法,每个test都运行一次
"class":作用于整个类,每个class的所有test只运行一次
"module":作用于整个模块,每个module的所有test只运行一次
"session:作用于整个session(慎用),每个session只运行一次
params:(list类型)提供参数数据,供调用标记方法的函数使用
autouse:是否自动运行,默认为False不运行,设置为True自动运行
import pytest
# --------------------------- 通过参数引用 函数级别 传入before的测试方法执行前会执行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
# --------------------------- 通过函数引用 函数级别 TestBBB类中每个测试方法执行前都会执行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
# --------------------------- 设置为默认运行 函数级别 每个测试方法执行前都会自动执行------------------------------
@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
# --------------------------- 设置作用域 默认是函数级别------------------------------
# class级别 TestDDD类中测试方法执行前执行一次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")
# 拿到返回值做一次断言 会执行3次
assert need_data != 2
# 注意:@pytest.mark.usefixtures("need_data") 这种方式拿不到返回值
@pytest.mark.usefixtures("need_data")
def test_abc222(self):
print("------->test_abc222")
assert False
跳过测试函数
方法:
skipif(condition, reason=None)
参数:
condition:跳过的条件,必传参数
reason:标注原因,必传参数
使用方法:
@pytest.mark.skipif(condition, reason="xxx")
# 跳过测试函数test_b
@pytest.mark.skipif(condition=True, reason="就是不想执行")
def test_b(self):
print("------->test_b")
assert 0
标记为预期失败函数
方法:
xfail(condition=None, reason=None, raises=None, run=True, strict=False)
常用参数:
condition:预期失败的条件,必传参数
reason:失败的原因,必传参数
使用方法:
@pytest.mark.xfail(condition, reason="xx")
# 标记为预期失败函数test_c
@pytest.mark.xfail(condition=2 > 1, reason="标注为预期失败")
def test_c(self):
print("------->test_c")
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次
# --------------------------- 单个参数 ---------------------------------
class TestAAA(object):
def setup_class(self):
print("------->setup_class")
def teardown_class(self):
print("------->teardown_class")
@pytest.mark.parametrize("a", [3, 6]) # a参数被赋予两个值,函数会运行两遍
def test_a(self, a): # 参数必须和parametrize里面的参数一致
print("test data:a=%d" % a)
assert a % 3 == 0
# --------------------------- 多个参数 ---------------------------------
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")
# 参数a,b均被赋予两组值,函数会运行两遍
# @pytest.mark.parametrize("a,b", [(1, 2), (0, 3)])
@pytest.mark.parametrize("a,b", get_test_data())
# 参数必须和parametrize里面的参数一致
def test_b(self, a, b):
print("test data:a=%d,b=%d" % (a, b))
assert a + b == 3
数据驱动
- yaml
- yaml文件格式
语法规则
1.大小写敏感
2.使用缩进表示层级关系
3.缩进时不允许使用Tab键,只允许使用空格。
4.缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
- 读取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)
常用参数:
data:写入数据类型为字典
stream:打开文件对象
encoding='utf-8' # 设置写入编码格式
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)
- 锚点&和引用*
锚点:标注一个内容,锚点名称自定义
引用:使用被标注的内容<<: *锚点名
data.yaml
data: &imp
value: 456
name:
value1: 123
<<: *imp # "<<:" 合并到当前位置,"*imp" 引用锚点imp
转换为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
# 在执行命令目录生成report文件夹,文件夹下包含xml文件 - xml转html工具安装
- Mac版本
(1)brew install allure
(2)进入report上级目录执行命令:allure generate report/ -o report/html
(3)report目录下会生成index.html文件,即为可视化报告 - windows版本
(1)下载压缩包allure-2.5.0.zip 解压
(2)将压缩包内的bin目录配置到path系统环境变量
(3)进入report上级目录执行命令:allure generate report/ -o report/html
(4)report目录下会生成index.html文件,即为可视化报告
添加测试步骤
@allure.step(title="测试步骤001")
添加测试描述
allure.attach('描述', '我是测试步骤001的描述~~~')
添加严重级别
@pytest.allure.severity(Severity)
参数 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