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用例执行结果情况

image

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:用例结果模拟测试
2)再标记(装饰器) @pytest.mark.web @pytest.mark.ui @pytest.mark.ut @pytest.mark.login @pytest.mark.pay @pytest.mark.add 3)后筛选 pytest -m web 4) 用例和标记的关系:多对多

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文件

    1. 作为注释符

    1. 缩进. 使用2个空格
    1. 成员表示
    • ‘-’表示列表成员
    • ‘:’表示字典成员
  • 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':{},
    }
)
**16.2 断言响应** 1.响应里面有什么 2.响应如何断言 3.从http协议抓包角度,响应由三部分组成 【1】行:状态码 【2】头:响应头(键值对) 【3】body体:响应内容 响应断言器: pip install responses_validator
点击查看代码
### 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 框架落地封装

posted @ 2026-02-24 10:53  jake瑞  阅读(81)  评论(0)    收藏  举报