当前位置: 首页 > news >正文

接口自动化-pytest

目录

1.介绍

2.pytest对比和优势

主流 Python 测试框架对比表

3.pytest安装

4.pytest

4.1传统的方式

4.2pytest测试

4.3pytest格式

4.4查看pytest文档

4.5 pytest命令参数

4.6pytest配置文件

4.7前后置

4.8断言

4.8参数化

4.9fixture

4.10 yield fixture

4.11fixtue带参数

4.11conftest.py 与 @pytest.fixture 结合实现全局前后置应用

4.12通过 params 实现参数化

5.总结


1.介绍

之前介绍了request,有了 requests 库,可以实现对接口发起 HTTP 请求。然而在自动化测试中,我们需要编写大量的测试用例,这些用例的组织、执行和管理则需要借助其他更强大的框架 ——pytest 框架。

requests 库专注于 HTTP 请求的发送,而 pytest 框架则提供了测试用例的组织、执行和管理功能。

pytest 是一个非常流行且高效的 Python 测试框架,它提供了丰富的功能和灵活的用法,使得编写和运行测试用例变得简单而高效

pytest 官方文档:Get Started - pytest documentation

2.pytest对比和优势

主流 Python 测试框架对比表

维度unittest(Python 内置)pytestRobot Framework
安装方式无需安装(Python 标准库)pip install pytestpip install robotframework
语法风格基于类(需继承TestCase函数式或面向对象(无需样板代码)关键字驱动(表格化用例)
断言方法self.assertEqual()原生assert表达式关键字断言(如Should Be Equal
参数化支持subTest或第三方库内置@pytest.mark.parametrize数据驱动(Test Template
插件生态少(依赖扩展库如HTMLTestRunner丰富(如pytest-htmlpytest-xdistallure-pytest一般(需安装额外库如RequestsLibrary
测试报告需插件生成报告支持多格式报告(HTML、Allure 等)自带详细日志和报告
学习曲线中等(需熟悉 xUnit 模式)低(语法简洁)高(需掌握关键字和语法)
BDD 支持不支持支持(通过pytest-bdd插件)支持(通过robotframework-bdd
适用场景简单项目或遗留系统维护复杂项目、高扩展性需求团队协作、非技术人员参与
pytest 核心优势详细说明
简单易用语法简洁清晰,编写测试用例友好,几乎可在几分钟内上手。
强大的断言支持支持原生 assert 表达式,无需记忆复杂的断言方法,且能智能报告断言失败的中间值,便于问题定位。
支持参数化测试通过内置的 @pytest.mark.parametrize 装饰器,允许使用不同的参数多次运行同一个测试函数,大幅提高测试效率。
丰富的插件生态系统拥有大量插件(如 pytest-html 生成 HTML 报告、pytest-xdist 实现并行执行、allure-pytest 生成美观报告等),可扩展覆盖测试、失败用例重跑等功能;同时支持与 Selenium、Requests、Appium 等工具结合,实现 Web、接口、App 自动化测试。
灵活的测试控制允许跳过指定用例、标记预期失败的用例,还支持重复执行失败的用例,提升测试灵活性。
多样化的测试组织方式支持函数式测试和面向对象测试(无需继承特定类),也可通过类对测试用例进行分组,便于测试管理。
内置实用功能提供如临时目录(tmp_path)等内置 fixtures,方便处理测试资源;支持通过 pytest.raises 断言异常场景,满足各类测试需求。

3.pytest安装

版本要求:安装 pytest 8.3.2 需要 Python 版本在 3.8 及以上。

安装命令

pip install pytest==8.3.2
  • 不同 pytest 版本支持的最低 Python 版本对应表

    pytest 版本最低 Python 版本
    8.0+3.8+
    7.1+3.7+
    6.2 - 7.03.6+
    5.0 - 6.13.5+
    3.3 - 4.62.7、3.4+
     

    若当前 Python 版本低于 3.8,可根据上表选择适配的 pytest 版本进行安装。

4.pytest

4.1传统的方式

def tes1():print('test1')def tes2():print('test2')def tes3():print('test3')
import test03if __name__ == '__main__':test03.tes1()test03.tes2()test03.tes3()

4.2pytest测试

class Testaaa():def test01(self):print("test01")def test02(self):print("test01")def test03(self):print("test01")def test04(seelf):print("test01")

pytest可以自动识别测试用例,不用再编写main函数并调用测试用例

但是用例要按照一定格式,不然识别不出测试用例。

4.3pytest格式

pytest 测试用例的命名规范如下:

  1. 文件名必须以 test_ 开头或者_test 结尾。
  2. 测试类必须以 Test 开头,并且不能包含 __init__ 方法。
  3. 测试方法必须以 test 开头。

注意:Python类中不可以添加 init 方法法

class Test01():def __init__(self):print("init")def test01_01(self):print("test01")

由于 pytest 的测试收集机制,测试类中不可以定义 __init__ 方法。

pytest 采用自动发现机制来收集测试用例:

  • 它会自动实例化测试类
  • 调用其所有以 test 结尾的方法作为测试用例

如果测试类中定义了 __init__ 方法,那么当 pytest 实例化该类时:

  • __init__ 方法会被调用
  • 这可能掩盖测试类的实际测试逻辑
  • 可能引入额外的副作用,影响测试结果的准确性

为了避免使用 __init__ 方法,建议在 pytest 中使用其他替代方案:

  1. 使用 setUp () tearDown () 方法
  2. 使用类属性
  3. 使用 fixture 函数

4.4查看pytest文档

pytest -h

4.5 pytest命令参数

pytest 提供了丰富的命令行选项来控制测试的执行,以下是一些常用的 pytest 命令行参数及其使用说明:

命令描述备注
pytest在当前目录及其子目录中搜索并运行测试
pytest -v增加输出的详细程度
pytest -s显示测试中的 print 语句
pytest test_module.py运行指定的测试模块
pytest test_dir/运行指定目录下的所有测试
pytest -k <keyword>只运行测试名包含指定关键字的测试
pytest -m <marker>只运行标记为指定标记的测试
pytest -q减少输出的详细程度
pytest --html=report.html生成 HTML 格式的测试报告需要安装 pytest-html 插件
pytest --cov测量测试覆盖率需要安装 pytest-cov 插件

pytest .\tests\test_aaa.py 

 pytest .\tests\test_aaa.py -sv

4.6pytest配置文件

参数解释
addopts指定在命令行中默认包含的选项
testpaths指定搜索测试的目录
python_files指定发现测试模块时使用的文件匹配模式
python_classes指定发现测试类时使用的类名前缀或模式
python_functions指定发现测试函数和方法时使用的函数名前缀或模式
norecursedirs指定在搜索测试时应该避免递归进入的目录模式
markers定义测试标记,用于标记测试用例

[pytest]
# 默认命令行参数:详细输出 + 显示print内容
addopts = -vs# 测试用例所在目录
testpaths = tests# 测试文件匹配规则
python_files = test_*.py *_test.py# 测试类命名规则
python_classes = Test*# 测试函数命名规则
python_functions = test*# 不搜索的目录
norecursedirs = .git venv node_modules build# 注册测试标记
markers =smoke: 核心功能冒烟测试regression: 回归测试用例slow: 执行时间较长的测试api: 接口测试用例
目录名称含义为何在 pytest 中跳过搜索
.gitGit 版本控制系统的核心目录,存储项目的版本历史、分支信息、提交记录等元数据与代码功能无关,不含测试用例,搜索会浪费时间,可能误判文件为测试用例
venvPython 虚拟环境目录,用于隔离项目的依赖包(避免与系统全局依赖冲突)包含第三方库代码(非项目自身测试用例),搜索会扫描大量无关文件,降低测试执行效率
node_modulesNode.js 项目的依赖目录,存储通过 npm 或 yarn 安装的 JavaScript 依赖包若为 Python 与前端混合开发项目,该目录仅含前端依赖,与 Python 测试用例无关,无需扫描
build存放编译、打包生成的文件(如 Python 项目的可执行文件、临时编译产物等)是自动生成的二进制或中间文件,不含源代码或测试用例,搜索无意义
import pytest@pytest.mark.api
class Testaaa():def test01(self):print("test01")def test02(self):print("test01")def test03(self):print("test01")def test04(seelf):print("test01")@pytest.mark.slow
class Testbbb():def test01(self):print("test01")def test02(self):print("test01")def test03(self):print("test01")def test04(seelf):print("test01")
pytest -m "api" 

 pytest -m "api and not slow"

pytest -m "api and not slow"

4.7前后置

之前的问题:使用 pytest 框架,测试类中不可以添加 init () 方法,如何进行数据的初始化?

在测试框架中,前后置是指在执行测试用例前和测试用例后执行一些额外的操作,这些操作可以用于设置测试环境、准备测试数据等,以确保测试的可靠性。

pytest 框架提供三种方法做前后置的操作:

  1. setup_method teardown_method:这两个方法用于类中的每个测试方法的前置和后置操作。
  2. setup_class teardown_class:这两个方法用于整个测试类的前置和后置操作。
  3. fixture:这是 pytest 推荐的方式来实现测试用例的前置和后置操作。fixture 提供了更灵活的控制和更强大的功能。(后续在 fixture 中详细讲解
class Test05():def setup_method(self, method):print("setup_method")def test05_01(self):print("test05_01")def test05_02(self):print("test05_02")def test05_03(self):print("test05_03")def teardown_method(self):print("teardown_method")

4.8断言

断言(assert)是一种调试辅助工具,用于检查程序的状态是否符合预期。如果断言失败(即条件为假),Python 解释器将抛出一个 AssertionError 异常。断言通常用于检测程序中的逻辑错误。

pytest 允许你在 Python 测试中使用标准的 Python assert 语句来验证预期和值。

基本语法:

assert 条件, 错误信息

  • 条件:必须是⼀个布尔表达式。
  • 错误信息:当条件为假时显示的错误信息,可选。
def test_assert():#断言列表expect_list=[1,3.14,"hello world"]actual_list=[1,3.14,"hello world"]assert expect_list==actual_list#断言元组expect_list = (1, 3.14, "hello world")actual_list = (1, 3.14, "hello world")assert expect_list == actual_list#断言集合expect_set = {1, 2, 3, 'apple'}actual_set = {1, 2, 3, 'apple'}assert expect_set == actual_setdef divide(a, b):assert b != 0, "除数不能为0"return a / b# 正常情况print(divide(10, 2))  # 输出 5.0# 触发断⾔print(divide(10, 0))  # 抛出 AssertionError: 除数不能为0

免费学习API资源:http://jsonplaceholder.typicode.com/

测试json结果

import requestsdef test():url="http://jsonplaceholder.typicode.com/posts/1"expect_data={"userId": 1,"id": 1,"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit","body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}r=requests.get(url=url)assert r.json()==expect_data

测试id结果

import requestsdef test02():url="http://jsonplaceholder.typicode.com/posts"r=requests.get(url=url)assert r.json()[0]["id"]==1assert r.json()[1]["id"] == 2assert r.json()[2]["id"] == 3

测试包含字段

def test02():url="http://jsonplaceholder.typicode.com/posts"r=requests.get(url=url)text="qui est esse"assert text in r.text

4.8参数化

参数化设计是自动化设计中的一个重要组成部分,它通过定义设计参数和规则,使得设计过程更加灵活和可控。

pytest 中,内置的 pytest.mark.parametrize 装饰器可实现对测试函数参数的参数化,其基本格式如下:

import pytest@pytest.mark.parametrize("参数名1, 参数名2, ...", [(参数值1, 参数值2, ...), (参数值1, 参数值2, ...), ...])
def test_函数名(参数名1, 参数名2, ...):# 测试逻辑pass

注意:找不到包,要在设置将包设置不排除在外。

示例:

单参数:

import pytest@pytest.mark.parametrize("data1", (1,2,3,4))
def test03(data1):print(data1)

多参数:

import pytest@pytest.mark.parametrize("data1, data2", [(1, 4), (2, 5), (3.14, 6.23),("hello","pytest")])
def test03(data1, data2):print(data1, data2)

参数运算:

import pytest@pytest.mark.parametrize("test_input,expected ", [(1*42, 42), (2+5, 7), (4/2, 2),(1-1,0),("hello","hello")])
def test04(test_input,expected):assert test_input == expected@pytest.mark.parametrize("n,expected ", [(1, 2), (3, 4)])
class Test_n:def test05(self,n,expected):assert n+1 == expecteddef test06(self,n,expected):assert (n*1) + 1 == expected

要对模块中的所有测试进⾏参数化,你可以将 pytestmark 全局变量赋值:

import pytestpytestmark = pytest.mark.parametrize("n",(1, 2))class Test_01():def test01(self,n):print(n)def test02(self,n):print(n)class Test_02():def test03(self,n):print(n)def test04(self,n):print(n)

自定义参数化数据源:

import pytest
def data_provider():return ["hello",1,1+2,3.14]pytestmark = pytest.mark.parametrize("data",data_provider())
def test_06(data):print(data)

除了使用 @pytest.mark.parametrize 实现测试函数的参数化外,pytest.fixture() 也支持对 fixture 函数进行参数化。

4.9fixture

pytest 中的 fixture 是一种强大的机制,用于提供测试函数所需的资源或上下文。它可以用于设置测试环境、准备数据等。以下是 fixture 的一些核心概念和使用场景。

未调用fixture

def fixture_01():print("第⼀个fixture标记的⽅法")
def test_01():fixture_01()print("第⼀个测试⽤例")

调用fixture

import pytest@pytest.fixture
def fixture_01():print("第⼀个fixture标记的⽅法")
def test_01(fixture_01):print("第⼀个测试⽤例")

未标记 fixture 方法的调用与 fixture 标记的方法调用完全不一样,前者需要在方法体中调用,而后者可以将函数名作为参数进行调用。

测试脚本中存在很多重复的代码公共的数据对象时,使用 fixture 最为合适。

 fixture嵌套

import pytest
@pytest.fixture
def login():print("\n登陆")def test_list(login):print("访问列表⻚")def test_detail(login):print("访问详情⻚")

# test_append.py的内容 import pytest
import pytest
@pytest.fixture
def first_entry():return "a"
# 安排
@pytest.fixture
def order(first_entry):return [first_entry]def test_string(order): # ⾏动order.append("b")# 断⾔assert order == ["a", "b"]


import pytest
class Fruit:#这里不是测试类可以用__init__def __init__(self, name):self.name = namedef __eq__(self, other):return self.name == other.name
@pytest.fixture
def my_fruit():return Fruit("apple")
@pytest.fixture
def fruit_basket(my_fruit):return [Fruit("banana"), my_fruit]def test_my_fruit_in_basket(my_fruit, fruit_basket):assert my_fruit in fruit_basket

4.10 yield fixture

当我们运行测试时,我们希望确保它们能够自我清理,以便它们不会干扰其他测试(同时也避免留下大量测试数据来膨胀系统)。pytest 中的 fixture 提供了一个非常有用的拆卸系统,它允许我们为每个 fixture 定义具体的清理步骤。

“Yield” fixture 使用 yield 而不是 return 。有了这些 fixture ,我们可以运行一些代码,并将对象返回给请求的 fixture/test ,就像其他 fixture 一样。唯一的不同是:

return 被替换为 yield

fixture 的任何拆卸代码放置在 yield 之后。

一旦 pytest 确定了 fixture 的线性顺序,它将运行每个 fixture 直到它返回或 yield ,然后继续执行列表中的下一个 fixture 做同样的事情。

测试完成后,pytest 将逆向遍历 fixture 列表,对于每个 yield 的 fixture ,运行 yield 语句之后的代码。

示例:

不返回数据

import pytest
@pytest.fixture
def operator():print("初始化操作")yieldprint("\n清理数据")
def test01(operator):print("第一个测试用例")

返回数据

import pytest
@pytest.fixture
def operator():print("初始化操作")yield 100print("\n清理数据")
def test01(operator):print(operator+100)assert operator == 100

import pytest
@pytest.fixture
def file_read():print("\n打开文件句柄")fo = open("log.txt", "r", encoding="utf-8")yield foprint("关闭文件句柄")fo.close()@pytest.fixture
def file_wirte():print("打开文件句柄")fo=open("log.txt","w",encoding="utf-8")yield foprint("\n关闭文件句柄")def test_file(file_read,file_wirte):w=file_wirtew.write("hello")w.close()r=file_readstr=r.read()print(str)

4.11fixtue带参数

pytest.fixture(scope='', params='', autouse='', ids='', name='')

scope 参数:用于控制 fixture 的作用范围,决定 fixture 的生命周期。可选值有:

scope 值说明
function(默认)每个测试函数都会调用一次 fixture
class在同一个测试类中共享这个 fixture
module在同一个测试模块(一个文件里)中共享这个 fixture
session整个测试会话中共享这个 fixture。

autouse 参数:默认为 False 。若设为 True ,每个测试函数会自动调用该 fixture ,无需显式传参。

params 参数:用于参数化 fixture ,支持列表传入。每个参数值会让 fixture 执行一次,类似 for 循环。

ids 参数:与 params 配合,为每个参数化实例指定可读标识符(给参数取名)。

name 参数:为 fixture 显式设名。若用了 name ,测试函数需用此名称引用 fixture(给 fixture 取名)。

scope="function"

import pytest@pytest.fixture(scope="function")
def fixture_01():print("\n初始化")yieldprint("\n清理")class TestCase():def test_01(self, fixture_01):print("第一个测试用例")def test_02(self, fixture_01):print("第二个测试用例")

scope="class"

import pytest@pytest.fixture(scope="class")
def fixture_01():print("\n初始化")yieldprint("\n清理")class TestCase1():def test_01(self, fixture_01):print("第一个测试用例")def test_02(self, fixture_01):print("第二个测试用例")class TestCase2():def test_01(self, fixture_01):print("第一个测试用例")def test_02(self, fixture_01):print("第二个测试用例")

scope="module"

scope 默认为 function,这里的 function 可以省略不写。当 scope="function" 时,每个测试函数都会调用一次 fixture。当 scope="class" 时,在同一个测试类中,fixture 只会在类中的第一个测试函数开始前执行一次,并在类中的最后一个测试函数结束后执行清理。

  • scope="module"、scope="session" 时,可用于实现全局的前后置应用,这里需要多个文件配合。

4.11conftest.py 与 @pytest.fixture 结合实现全局前后置应用

@pytest.fixture conftest.py 文件结合使用,能够在多个测试模块(.py 文件)中共享前后置操作。这种方式可在整个测试项目中定义和维护通用的前后置逻辑,让测试代码更模块化、更易维护。

规则:

  •  conftest.py  是单独存放夹具配置的文件,名称固定且不可修改。
  • 可在项目的不同目录下创建多个 conftest.py 文件,每个文件仅对其所在目录及子目录下的测试模块生效。
  • 在不同模块的测试中使用 conftest.py 的前后置功能时,无需进行任何 import 导入操作。

作用:

使不同 .py 文件可以使用同一个 fixture 函数

autouse 默认为 False,即当前的 fixture 需要手动显式调用,在该案例之前我们默认使用的都是 autouse=False

autouse=True 时,fixture 会在所有测试函数执行之前自动调用,无论这些测试函数是否显式地引用了该 fixture

scope="autouse"

4.12通过 params 实现参数化

@pytest.fixture(params=["1","1+2","a","3.14"])
def data_provider(request):return request.paramdef test_data(data_provider):print(data_provider)

如果测试场景主要涉及简单的参数传递,且不需要复杂的资源管理,建议使用 parametrize,因为它更简单直接;如果测试需要动态加载外部数据,或者需要管理复杂的测试资源(如数据库连接、文件操作等),建议使用 fixture。在某些情况下,也可以结合使用 parametrize fixture,以充分利用两者的优点。

总结来说, parametrize 更适合简单场景,而 fixture 更适合需要动态数据和资源管理的复杂场景。

5.总结

http://www.dtcms.com/a/319674.html

相关文章:

  • OpenAI 开源模型 gpt-oss 正式上线微软 Foundry 平台
  • 网络可视,运维无忧:分钟级定位,告别盲目扩容
  • 从零开始构建情绪可视化日记平台 - React + TypeScript + Vite
  • CPTS Remote 复现
  • Redisson中的分布式锁
  • 自动化办公革命:3小时完成8小时工作量
  • 钢卷矫平机科普:把“弯曲的记忆”清零
  • 人工智能与能源:AI 驱动的能源产业变革
  • MCU-基于TC397的双BootLoader设计方案
  • 关于vue2中对接海康摄像头以及直播流rtsp或rtmp,后台ffmpeg转码后通过ws实现
  • 【linux】vmware中ubuntu无法上网
  • 关于 cadence导入原理图出现ERROR(ORCAP-1192)错误 的解决方法
  • 蓝桥杯算法之搜索章 - 3
  • HarmonyOS分布式开发实战:打造跨设备协同应用
  • C 语言主控开发与显控开发能力体系及技术栈详解,STM32、QT、嵌入式、边缘系统显示
  • 【Vite】Vite 构建 React 项目中 Select a variant 配置选择指南:标准版 vs SWC
  • shell脚本while只循环一次,后续循环失效
  • 解码算法:维特比算法(Viterbi)在SMT中的应用
  • 开发避坑指南(20) :MyBatis操作Oracle插入NULL值异常“无效列类型1111“解决方案
  • 金仓KingbaseES逻辑架构,与Oracle/MySQL对比
  • Windows Oracle 11 g dmp数据库恢复笔记
  • 一种基于CEEMDAN-小波阈值联合降噪-快速谱峭度(FSK)/基尼谱Ginigram/Autogram的故障诊断 Matlab
  • 【已解决】-bash: mvn: command not found
  • [Oracle] FLOOR()函数
  • Oracle 12c + Pl/Sql windows系统下表空间创建、迁移,dmp备份导入,数据库字符集更改
  • 7. 什么是事件委托
  • 试用一个用v语言编写的单文件数据库vsql
  • RepoCoder:仓库级代码补全的迭代检索生成框架解析与应用前沿
  • 【FreeRTOS】(号外)任务间通讯2: 信号量- Counting Semaphore
  • NFS 服务器与iSCSI 服务器