01 | 简介
Pytest是一个非常成熟的全功能的Python测试框架,主要有以下特点:
- 简单灵活,容易上手,文档丰富
- 支持参数化,可以细粒度地控制测试用例
- 支持简单的单元测试与复杂的功能测试,还可以用来做Selenium、Appnium、Requests等UI、接口自动化测试
- 支持很多第三方插件,并且可以自定义扩展(插件下载地址:http://plugincompat.herokuapp.com/)
- 支持测试用例的跳过与失败重试
- 可以很好的与CI工具结合,比如Jenkins
02 | 安装
pip install pytest
03 | 简单使用
import pytest
def test01():
print('第一条用例')
assert True
def test02():
print("第二条用例")
assert False
if __name__=="__main__":
# -s:显示用例中的输出
# -v:输出更详细的用例执行信息
# __file__:本文件
pytest.main(["-s", "-v", __file__])
04 | 初始化&结束
4.1 函数级别
每执行一个测试用例就执行一次初始化&结束方法
import pytest
class Test:
def setup(self):
print("初始化")
def teardown(self):
print("结束")
def test01(self):
print('第一条用例')
assert True
def test02(self):
print("第二条用例")
assert False
if __name__=="__main__":
pytest.main(["-s",__file__])
4.2 类级别
不管类中有多少条用例需要执行,仅仅只执行一次初始化&结束方法
import pytest
class Test:
def setup_class(self):
print("初始化")
def teardown_class(self):
print("结束")
def test01(self):
print('第一条用例')
assert True
def test02(self):
print("第二条用例")
assert False
if __name__=="__main__":
pytest.main(["-s",__file__])
05 | 常用插件
5.1 测试报告
安装测试报告插件
pip install pytest-html
在项目目录下创建 pytest.ini 配置文件,配置文件中新增测试报告存储路径
[pytest]
addopts = --html=./report/report.html # 在当前目录下创建report目录存放测试报告
5.2 失败重试
安装
pip install pytest-rerunfailures
全局重试:在配置文件中增加重试参数,控制所有用例失败后的重试及重试等待时间
[pytest]
addopts = --html=./report/report.html --reruns 3 --reruns-delay 2
# --reruns n,n是一个整数,表示重试次数
# --reruns-delay n,n是一个整数,表示重试等待时间,单位是s
测试报告中可以看出失败用例的重试情况
单个重试:在某个用例函数中增加装饰器,注意需要将 全局重试 的配置注释掉
# reruns指定重试次数,reruns_delay指定重试间隔
@pytest.mark.flaky(reruns=2, reruns_delay=1)
def test02(self):
print("第二条用例")
assert False
5.3 多进程并发执行
安装插件
pip install pytest-xdist
多CPU并行测试
pytest -n 并行测试数量
5.4 多重断言
使用pytest进行断言判断的时候,为了用例的精准性,经常会多个方面进行断言,比如如下:
- 断言1:断言响应的http的状态
- 断言2:断言响应返回的code值
- 断言3:断言响应返回的json中的data字段是否符合预期。
如果使用原生python的assert,就会遇到一个断言失败则全部失败的情况。比如说,断言1结果为Failed,那么断言2和断言3都不会被执行。我们希望断言2和断言3继续执行,这样我们能获取更多的断言结果来判断出接口哪里出了问题,能够更好地进行问题定位,这时候就可以使用pytest-assume插件来实现。
安装pytest-assume
pip install pytest-assume
使用案例
import pytest
def test_add2():
pytest.assume(1 + 4 == 5)
pytest.assume(1 + 3 == 3)
pytest.assume(2 + 5 == 7)
pytest.assume(2 + 5 == 9)
print("测试完成")
if __name__=="__main__":
pytest.main(["-s", "-v", __file__])
执行结果如下图,可以看出总共执行了四个断言,其中失败的断言有2个
5.5 美化执行结果
在我们进行自动化测试的时候,用例往往是成百上千,执行的时间是几十分钟或者是小时级别。有时,我们在调试那么多用例的时候,不知道执行到什么程度了,而pytest-sugar插件能很好解决我们的痛点。
安装pytest-sugar插件
pip install pytest-sugar
在命令行中执行用例文件,执行结果如下图,用例执行时会有进度条展示:
06 | 数据驱动
在 pytest 中,数据驱动是经由 pytest 自带的 pytest.mark.parametrize() 来实现的。 pytest.mark.parametrize 是 pytest 的内置装饰器,它允许你在 function 或者 class 上定义多组参 数和 fixture 来实现数据驱动。
6.1 单个数据
import pytest
class Test:
data = ["小明", "小方"]
@pytest.mark.parametrize("name", data) # 注意,函数中要定义好对应的形参接收数据
def test01(self, name):
print('第一条用例')
print("大家好,我叫{}".format(name))
assert True
if __name__=="__main__":
pytest.main(["-s",__file__])
执行结果
01pytest.py 第一条用例
大家好,我叫小明
.第一条用例
大家好,我叫小方
.
6.2 多个数据
import pytest
class Test:
data = [("小明", 18), ("小方", 28)]
@pytest.mark.parametrize(("name","age"), data)
def test01(self, name, age):
print('第一条用例')
print("大家好,我叫{},我今年{}岁".format(name, age))
assert True
if __name__=="__main__":
pytest.main(["-s",__file__])
执行结果
01pytest.py 第一条用例
大家好,我叫小明,我今年18岁
.第一条用例
大家好,我叫小方,我今年28岁
.
6.3 Excel数据驱动
Excel数据文件如下图:
代码如下:
import pytest
from openpyxl import load_workbook
class ParseExcel:
def __init__(self, excel_path, sheet_name):
# 加载excel文件,注意格式为xlsx
self.wb = load_workbook(excel_path)
# 指定工作表
self.sheet = self.wb[sheet_name]
def get_data_from_sheet(self):
"""从excel文件中读取指定工作表中所有数据行"""
# 定义空列表保存从excel读取到的数据
data_list = []
# 循环遍历工作表中所有的单元格(按行)
for line in self.sheet.rows:
# 定义临时列表,储存每一行的数据
temp_list = []
# 添加数据行中的第一个单元格数据
temp_list.append(line[0].value)
# 添加数据行中的第二个单元格数据
temp_list.append(line[1].value)
# 将temp_list添加到data_list中
data_list.append(temp_list)
# 返回所有数据行,除了第一行,第一行一般是表头
return data_list[1:]
# 测试excel读取功能
# if __name__ == '__main__':
# excel = ParseExcel("data.xlsx", "Sheet1")
# print(excel.get_data_from_sheet())
class Test:
# 创建excel读取对象
excel = ParseExcel("data.xlsx", "Sheet1")
# 读取数据
excel_data = excel.get_data_from_sheet()
# 注意在使用装饰器参数化时,多个参数要用元组包起来
# 并且装饰器里面的参数要和case里面的参数保持一致
@pytest.mark.parametrize(('username', 'password'), excel_data)
def test(self, username, password):
print(f"用户名是:{username}")
print(f"密码是:{password}")
if __name__ == '__main__':
pytest.main(["-s", "-v", __file__])
执行结果如下:
07 | 配置文件
在项目中,需要同时执行多个用例时,可以采用配置文件的方式来运行多个测试用例(当然也可以单独编写执行文件),配置文件中可以定义的运行规则如下:
[pytest]
addopts = --html=./report/report.html --reruns 2 --reruns-delay 2
testpaths = testcase
python_files = test*.py
python_classes = Test*
python_functions = test*
上面这些配置按照需求定义即可
定义完成后,可以直接在当前项目路径的命令行中输入 pytest 即可执行多个用例。
08 | Allure
Allure框架是一个灵活的轻量级多语言测试报告工具,它不仅以web的方式展示了简介的测试结果,而且允许参与开发过程的每个人从日常执行的测试中最大限度的提取有用信息
8.1 安装
pip install allure-pytest
8.2 下载
- GitHub下载地址:https://github.com/allure-framework/allure2/releases
- 百度云链接:https://pan.baidu.com/s/1NO1mqx7hbDx55P1zDsf48g ,提取码:31bn
8.3 解压
解压完成后,需要将allure中的bin目录添加至环境变量
验证allure
8.4 使用
- 使用pytest执行测试用例,并指定allure报告目录:pytest 文件名 —allure=./allure_report
- 在线生成allure报告:allure serve allure_report
- 生成本地allure报告:allure generate allure_report
报告效果图如下: