pytest测试框架
1、测试框架概念
- 抽象出来的工具集合,提供大量组件、工具和功能
- 1)用例发现:可以自动发现任何目录下的用例
- 2)用例管理:用例筛选
- 3)环境管理:前置操作或后置操作
- 4)用例执行:执行用例当中的步骤,并收集用例的结果
- 5)测试报告:将用例发现、用例执行及最终的结果生成一个完整的报告
1.1、主流编程语言测试框架
- java:junit和testng
- python:unittest和pytest
- unittest和pytest对比
![image]()
2、快速安装
- pip install pytest #普通安装
- pip install pytest -U #升级到最新版本
2.1、pytest三种启动方式
- 方法一:命令方式启动:pytest
- 方法二:代码方式启动
点击查看代码
import pytest
pytest.main()
- 方法三:鼠标方式启动(鼠标右键点击run)不建议
-(1)该方法是由pycharm软件提供的,并非pytest自身的
-(2)行为与前两种不一致,不适用复杂项目
2.2、pytest断言
- pytest在简单基础上,对断言进行了高级的封装(AST),对python数据结构的断言非常的友好
- 1)遵循python简单的学习方式
- 2)同时实现了很多高级特性
- 3)鼓励大家积极的使用断言(建议强制使用)
3、看懂结果
============================= test session starts =============================
platform win32 -- Python 3.14.0, pytest-9.0.2, pluggy-1.6.0
rootdir: D:\Pycharm2025\pytest自动化学习
plugins: anyio-4.12.1, Faker-40.1.2
collected 9 items
test_base.py .F [ 22%]
test_data.py FFFF..F [100%]
================================== FAILURES ===================================
__________________________________ test_fail __________________________________
def test_fail():
assert False
E assert False
test_base.py:6: AssertionError
__________________________________ test_str ___________________________________
def test_str():
a = 'aaaaaa'
b = 'aaaabc'
assert a == b
E AssertionError: assert 'aaaaaa' == 'aaaabc'
E
E - aaaabc
E + aaaaaa
test_data.py:6: AssertionError
=========================== short test summary info ===========================
FAILED test_base.py::test_fail - assert False
FAILED test_data.py::test_num - assert 1 == 2
FAILED test_data.py::test_str - AssertionError: assert 'aaaaaa' == 'aaaabc'
FAILED test_data.py::test_list - assert [1, 2, 3, 4, 5, 6] == [1, 2, 3, 5, 4, 7]
FAILED test_data.py::test_dict - AssertionError: assert {'a': 1, 'b':...c': 3...
FAILED test_data.py::test_bool - assert True == False
========================= 6 failed, 3 passed in 0.29s =========================
- 执行环境:python、pytest、pluggy版本、代码根目录、用例数量
- 执行过程:文件名称、用例结果、执行进度
- 失败详情:用例内容、断言提示
- 整体摘要:整体用例结果数量、结果情况、花费时间
- pytest用例执行结果情况

4、用例规则
4.1用例发现规则
- 测试框架在识别、加载用例的过程,称之为:用例发现
4.1.1 pytest用例发现步骤:
(1)遍历所有的目录,例外venv目录和‘.’开头的不在遍历范围内
(2)打开python文件,文件名称规则:‘test_’开头,'_test'结尾
(3)遍历所有Test开头类
(4)收集所有test_开头函数 或者 方法
4.2 用例内容规则
- (1)可调用的(函数、方法、类、对象)
- (2)名字'test_'开头
- (3)没有参数(参数有另外的含义)
- (4)没有返回值(默认为None)
- (5)变量可作为测试用例
4.3 练习
有函数add接收两个参数,并返回他们相加的结果,请为此编写测试用例
点击查看代码
def add(a,b):
return a+b
class TestAdd:
def test_int(self):
res = add(1,2)
assert res == 3
def test_str(self):
res = add('1','3')
assert res == '13'
def test_list(self):
res = add([1],[2,3,4])
assert res == [1,2,3,4]
def test_float(self):
res = add(1.2,3.4)
assert res == 4.6
5、配置框架
5.1 配置可以改变pytest默认规则
(1)命令行参数
(2)ini配置文件(在根目录中创建pytest.ini)
5.2 所有配置可以一键获取 pytest -h **
5.2.1 有那些参数
5.2.2 分别是什么方式
- 1) -开头的是:参数
- 2)小写字母开头:ini配置
- 3)大写字母开头:环境变量
5.3 常用参数
(1)-v:增加详细程度
(2)-s:在用例中正常的使用输入和输出**(允许用例中的输入和输出显示)
(3)-x:快速退出(当遇到失败的用例停止执行)
(4)-m:用例筛选
6、标记mark
6.1 标记作用
标记可以让用例与众不同,进而可以让用例被区别对待
6.1.1 用户自定义标记,只能实现用例筛选
6.1.1.1 用户自定义标记的操作步骤
1)先注册
在pytest.ini中注册
点击查看代码
[pytest]
markers =
api:接口测试
web:UI测试
ut:单元测试
login:登陆相关
pay:支付相关
add:用例结果模拟测试
6.1.2 框架内置标记(为用例增加特殊执行效果)
6.1.2.1 和用户自定义标记区别
(1)不需要注册,可以直接使用
(2)不仅可以筛选,还可以增加特殊效果
(3)不同的标记,增加不同的特殊效果
- skip:无条件跳过
- skipif:有条件跳过
- xfail:预期失败
- parametrize:参数化
- usefixtures:使用fixtures
7、数据驱动测试参数
数据驱动测试 = 参数化测试 + 数据文件
根据数据文件的内容,动态决定用例数量、内容
-
构造的csv数据信息
![image]()
-
获取csv数据的代码
点击查看代码
import csv
import os
import chardet
#检测文件编码
def detect_encoding(file_path):
with open(file_path, 'rb') as f:
result = chardet.detect(f.read())
return result['encoding']
#获取csv文件内容
def read_csv(filename):
# 获取当前文件的绝对路径
current_dir = os.path.dirname(os.path.abspath(__file__))
file_path = os.path.join(current_dir, filename)
file_encoding = detect_encoding(file_path)
with open(file_path, 'r', encoding=file_encoding) as f:
reader = csv.reader(f)
header = next(reader)
return list(reader)
- 数据驱动测试的代码信息
点击查看代码
@pytest.mark.ddt
@pytest.mark.parametrize(
'a,b,c',
read_csv("ddt.csv")
)
def test_ddt(self,a,b,c):
res = add(int(a),int(b))
assert res == int(c)
8、夹具fixtuer
- 在用例执行之前,执行之后,自动运行代码
场景: - 接口:之前:加密参数/之后:解密结果
- web:之前:启动浏览器/之后:关闭浏览器
- web:之前登陆账号/之后:退出/删除账号
8.1 创建fixtuer
-1)创建函数
-2)添加装饰器
-3)添加yield关键字
点击查看代码
@pytest.fixture
def f():
print(datetime.datetime.now(),'用例开始执行...')
#前置操作
yield
#后置操作
print(datetime.datetime.now(),'用例执行完成!!!')
** 8.2 使用fixture**
(1)在用例的参数列表中,加入fixture名字即可
def test_1(f):
pass
(2)给用例加上usefixtures装饰器标记
@pytest.mark.usefixtures("f")
def test_2():
pass
8.3 高级用法
- 自动使用:autouse=True :默认自动执行,测试用例无论是否使用了fixture,该fixture都会执行
- 依赖使用
(1)linux:使用linux进行编译
(2)git:使用git进行版本控制
(3)fixture:使用fixture进行前后置自动操作 - 返回内容:接口自动化测试封装:接口关联
(1)在yield后面携带fixture的传递的数据 - 范围共享
(1)默认范围:function(函数) @pytest.fixture(autouse=True,scope='funtion')
(2)全局范围:session(全局) @pytest.fixture(autouse=True,scope='session')- 使用conftest.py(第三方命名空间),将需要全局共享的fixture放在该文件中
点击查看代码
import datetime
import pytest
@pytest.fixture(scope='session')
def ff():
print('我也是fixture,但是被fixture使用')
@pytest.fixture(autouse=True,scope='session')
def f(ff):
print(datetime.datetime.now(),"用例开始执行")
#前置操作
yield 123#开始执行执行用例“123”为f的返回值
#后置操作
print(datetime.datetime.now(), "用例执行完成")
**#从python的语法来看,函数中可以有多个 yield,但pytest框架要求只能出现一个**
9、插件管理
-
pytest插件生态是pytest特别的优势之处
https://docs.pytest.org/en/stable/reference/plugin_list.html
9.1 插件类型 -
不需要安装:内置插件
-
需要安装:第三方插件
9.2 插件启用管理 -
启用:-p abc
-
禁用:-p no:abc
9.3 插件使用方式 -
参数
-
配置文件
-
fixture
-
mark:自动跳过、参数化
10 常用第三方插件 -
10.1 pytest-html
-
用途:生成html测试报告
-
安装:pip install pytest-html
-
使用方式1:参数化:pytest --html=report.html --self-contained-html
-
使用方式2:在pytest.ini中添加如下代码
addopts = --html=report.html --self-contained-html
![image]()
-
10.2 pytest-xdist (默认采用多进程)
-
用途:分布式执行
-
安装:pip install pytest-xdist
-
使用:-n N
-
说明:
-
只有在任务本身耗时较长,超出调用成本很多的时候,才有意义
-
分布式执行,有并发问题,资源竞争、乱序执行
-
10.3 pytest-rerunfailures
- 用途:用例失败之后,重新执行
- 安装:pip install pytest-rerunfailures
- 使用:Num:重试次数,Time:每次重试之间的间隔等待时间
参数:--reruns Num --reruns-delay Time
-
10.4 pytest-result-log
- 用途:把用例的执行结果记录的日志文件中
- 安装:pip install pytest-result-log
- 使用:通过配置文件实现
点击查看代码
log_file = ./logs/pytest.log
log_file_level = info
log_file_format = %(levelname)-8s %(asctime)s [%(name)s:%(lineno)s] : %(message)s
log_file_date_format = %Y-%m-%d %H:%M:%S
; 记录用例执行结果
result_log_enable = 1
; 记录用例分割线
result_log_separator = 1
; 分割线等级
result_log_level_separator = warning
; 异常信息等级
result_log_level_verbose = info
11、企业级测试报告
allure是一个测试报告框架
11.1 allure插件基本操作
- 安装
pip install allure-pytest - 配置:只能生成json元数据,指定元数据的的临时目录,并清除原有的数据
addopts = --alluredir=temps --clean-alluredir - 生成报告(需要安装allure)
- 方法1:终端执行用例后再执行生成报告命令
- pytest -m ut (执行测试用例)
- allure generate -o report -c temps (生成报告)
- 安装allure
https://github.com/allure-framework/allure2/releases/download/2.30.0/allure-2.30.0.zip - 解压至D盘根目录
Expand-Archive -Path D:\allure-2.30.0.zip -DestinationPath D:\allure -Force - 添加环境变量
在windows系统,高级系统设置,环境变量,系统变量,Path中添加D:\allure\allure-2.30.0\bin,重启系统生效 - 测试有效性
分别在pycharm和cmd命令行中执行allure --version - 方法2:通过脚本方式启动,自动生成报告
点击查看代码
import pytest
import os
pytest.main()
os.system("allure generate -o report -c temps ")
11.2 allure支持对用例进行分组和关联(敏捷开发术语)
@allure.epic #史诗,项目
@allure.feature #主题 模块
@allure.story #故事 功能
@allure.title #标题 用例
- 使用相同装饰器的用例,自动的并入同一组
点击查看代码
import allure
import pytest
@allure.epic('小象自动化学习')
@allure.feature('pytest自动化训练营')
@allure.story('mark标记和筛选')
@allure.title('实现筛选的用例')
@pytest.mark.ut
def test_a():
pass
@allure.epic('小象自动化学习')
@allure.feature('pytest自动化训练营')
@allure.story('fixture前置和后置')
@allure.title('使用fixture的用例')
@pytest.mark.ut
def test_b():
pass
12、Web自动化测试实践
- pytest仅进行用例管理,不会控制浏览器,需要借助新的工具:selenium
1.只了解selenium
2.所搜有关selenium的pytest插件 - 自己手动写fixture也可以实现selenium功能,示例
点击查看代码
from selenium import webdriver
@pytest.fixture(scope='session')
def selenium():
driver = webdriver.Chrome()
yield driver
driver.close()
13.测试框架要封装什么
封装:
- 隐藏细节
- 增加功能
- 优化功能
13.1 接口自动化封装: - 使用yaml作为用例,可以降低自动化门槛
- 自动请求接口、断言接口
- 自动在日志中记录http报文
- 自动生成allure测试报告
14.YAML文件格式
- YAML完全兼容JSON格式,并且支持python相似写法
重点: - 1.YAML完全兼容JOSN
- 2.是数据格式,不是编程语言
- 3.像Python一箱用以编辑和阅读
14.1 安装yaml模块
pip install pyyaml
14.2 编写yaml文件
-
-
作为注释符
-
-
- 缩进. 使用2个空格
-
- 成员表示
- ‘-’表示列表成员
- ‘:’表示字典成员
- 4.兜底规则:完全兼容json
点击查看代码
#这是我的第一个yaml文件
数字:
- 1
- -1
- 1.1
- 0.0
- 3.1415926
字符串:
- '123456789'
- 'abcdeftg'
- adjdkjfjf
空值: null #JSON写法
列表: [1,2,3,4,5,6,7,8] #JSON写法
字典: {'a':1,'b':2,'c':3} #JSON写法
14.3 加载yaml文件
点击查看代码
import yaml
def load_yaml(path):
with open(path, 'r+',encoding="utf-8") as f:
result = f.read()
data = yaml.safe_load(result)
return data
15.接口测试用例
15.1 设计用例内容
- 1.名字:请求首页的数据接口
- 2.标记【可选】
- 3.用例步骤
【1】请求接口:get https://www.baidu.com
【2】响应断言:status_code = 200
【3】提取变量:json()['code']
15.2 YAML表示用例
点击查看代码
#name:登陆成功用例
steps:
- request: #发送请求
method: POST
url: http://116.62.63.211/shop/api.php?application=app&application_client_type=weixin
params:
s: user/login
json: {
"accoounts":beifan_1105,
"pwd":"beifan_1105",
"type":"username"
}
- response: #断言响应
status_code: 200
json:
code: 0
msg: 登陆成功
data:
username: beifan_1105
- extract: #提取变量
token: [json,$.data.token]
16.封装接口自动化框架
16.1 请求接口
- 外部工具:requests
- 从http协议抓包角度,请求由三部分组成
【1】行:方法+地址(必填)
【2】头:请求头(键值对)
【3】body体:参数内容
点击查看代码
import requests
###1.方法
url = "http://116.62.63.211/shop/api.php"
###get方法
requests.get(url)
###post方法
requests.post(url)
###任意方法
requests.request('MOVE',url)
###2.头
mathod = 'post'
url = 'http://116.62.63.211/shop/api.php'
requests.request(
mathod,url,
headers={ #头必须是字符串字典
"1":'1',
"2":'b',
}
)
###3.参数
mathod = 'post'
url = 'http://116.62.63.211/shop/api.php'
requests.request(
mathod,url,
json={ #任意内容的字典
"1":1,
"b":[1,2,3,4],
'c':{},
}
)
点击查看代码
### rep就是响应
print(resp.status_code) #状态码
print(resp.headers) #影响头
print(resp.text) #响应正文
print(resp.json()) #响应正文转换成json
###断言单个内容是否正确
assert resp.status_code == 200
assert '美酒' in resp.text
assert resp.json()['data']['banner_list'][0]['name'] == "美酒"
##断言全部内容
validator(
resp,
status_code=200,
text='*美酒*',
json={
"data":{
"banner_list":[{"name":'美酒'}]
}
}
)
16.3 变量提取
- 基本原则
【1】JSON:JSONAPTH
【2】HTML:XPATH
【3】字符串:RE - RE可以兜底
示例1:JSONPATH提取变量
点击查看代码
from extract_utils import extract
var = extract(resp,'json','$..banner_list[0].name')
print(var)
16.4 框架落地封装




浙公网安备 33010602011771号