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

使用pytest对接口进行自动化测试

  上篇博客中讲述了什么是接口测试,已经自动化接口测试流程,这篇博客总结如何实现接口自动化测试

(一)requests


requests库是Python对HTTP通信的一个工具,将http协议操作封装成简单的接口,能够让我们高效的编写各种网络自动化任务

安装requests库
我们可以通过pip命令来进行安装,在控制板中输入

后面的版本号可以不必一致,可以去官网查看

我们看上面successfully就是安装成功了
那我们也可以通过另一个命令来查看当前项目有什么库

接下来我们看如何使用这个库

首先就是获取一个http中的get请求到指定url

我们看一下这个方法的参数,url都不陌生就是我们要请求的网址,params就是我们的参数类似于我们在postman中这个位置填的数据
**kwargs是常见可选参数,就比如我们可以在里面设置请求头,cookie等

使用时只需要调用get接口

传入对应参数,就会给我们返回一个response对象,该对象包含了服务器给我们返回的一系列信息


当然之前我们学习请求形式不止get请求还有post,put等请求,我们也可以调用对应方法来进行url请求

我们再点进去看他们的源码

我们发现调用的都是request方法,所以我们想访问url时,也可以直接调用request方法,只不过需要我们手动去传入method用什么方式访问
我们这里举个实例,我们选择用一个博客详情的接口来举例

其实和我们使用postman类似
但在上面我们传递参数发现,可以使用params,json,data来传参
那么三者有什么区别?

parasms:使用params传参,参数是在url上体现的传递的是简单的键值对,常用于get请求来获取对应数据,因为可见所以她不适合传入敏感数据
data:使用data传参,参数在data中,常用于put或者post请求传递的是表单类型的数据,但是data是不支持嵌套的。键值对用 & 连接

json:使用json传参,参数在data中,常用于put或者post请求传递的是json的数据,json是支持嵌套的

上面就是request库的一些操作及使用, requests库主要是发送http请求,但是对于测试执行和管理就没怎么涉及到,接下来我们会使用pytest来完成测试的组织,执行,管理功能。


(二)pytest


我们这里选择使用pytest接口来实现自动化接口测试,除了pytest我们也可使用其他框架比如unnittest或者robot framework等

我们选择pytest是因为她的语法较为简单,且插件比较多,我们可以通过下载插件来完成各种功能

pytest的安装

我们安装pytest有个版本对应表,不同版本的pytest有最低适配的python版本

下载操作和刚刚我们下载requests操作类似,都是使用pip install操作来下载

同样我们可以使用pip list来看

安装好后,我们来看一下有没有pytest框架对于代码编写的区别
有pytest

没有pytest

我们发现如果没有pytest框架,我们要执行一个方法,需要一个main函数,并在函数中调用,若我们有了pytest框架,我们可以直接运行该方法,但是我们需要遵守pytest的命名规范,下面就来说一下怎样命名是可以被pytest识别到的

pytest框架默认命名格则

文件名:文件名需要以test_**来命名或者**_test来命名
测试类:测试类必须以Test开头,并且不能有__init__方法
测试方法:测试方法必须以test**开头


init和构造方法


注:测试类中不推荐有init方法(类似于java中的构造方法),但是我们可以定义
之所以不推荐是因为init方法是无法访问pytest fixtures的(执行顺序为:init方法->fixtures->其他方法)
而且构造函数只负责初始化并没有对应清理机制,且每次测试方法都会创建新的实例。

这一点要做一下区分,此时在pytest中的init方法和构造方法是有很大区别的
就比如我们局一个简单的例子,我们在python正常类中写一个init方法和一个类方法,之后我们实例化一个对象并多次调用类方法

我们发现只会调用一次构造函数,在多次调用方法时,也不会再调用构造函数,而当我们在pytest中创建init方法,我们再来看一下

他的输出是这样的

init方法在每次测试方法执行前都会重新调用一次(这是因为每个测试方法都是新的实例,测试之间不共享状态)

以上就是我们为什么不推荐使用init方法,为了处理init方法初始化的作用,pytest给我们提供了其他的初始化方法比如:setup/teardowm或者使用fixture  这些在之后我们也会再讲
注:上面在pytest中使用init方法仅限pytest 4之后的版本,在pytest 4之前版本,是禁止任何类有init方法的

pytest命令参数


pytest提供了很多命令行来控制测试的执行,以下是一些常用的命令航参数以及说明
pytest:在当前目录和其子目录下找到符合命名规范的类和方法并运行测试
pytest -v:增加输出的详细程序
pytest -s:显示测试中的print语句
pytest test_module.py :运行指定的测试模块
pytest test_dir:运行指定目录下的所有测试
还有一些其他的命令

我们在自己项目中使用pytest命令如下:
会自动执行符合pytest命名规则的方法

但是我们看显示框,发现信息很少,如果我们要显示更全面的信息,需要使用-v

但是上面并没有打印出111,这是因为如果我们想显示print语句,我们需要-s

当我们想指定测试方法执行时,我们需要手动指定,或者手动点击
手动指定


我们发现每次要执行指令时,我们要手动输入很长的一段命令,如何解决这个问题呢?

我们就需要把相关配置参数,统一放到pytest配置文件中

pytest配置文件


需要手动在当前项目下创建pytest.ini文件(文本文件),以下时一些常见的配置选项


在配置文件中我们可以这样指定

这样就是说明,我们默认在后面加上-vs,搜索的py文件为test_*,搜索的类为Test*
此时我们执行pytest,我们发现可以自动打印出原来pytest -vs才能执行出的表现

当我们更改搜索的py文件名称时我们来看看表现


但是同时.ini是不区分大小写的,如果我们想区分大小写,我们可以使用yml文件(之后会说)

前后置方法


上面我们说pytest里面不推荐有些版本甚至不能使用init初始化
那如果我们想执行测试用例前后执行一些额外操作,我们就需要用pytest提供的三种额外方法做前后置操作
setup_method和teardown_method:这两个方法用于类中每个测试方法的前置与后置操作
setup_class和teardowm_class:这两个方法用于整个测试类的前置和后置操作
fixture:使用fixture是比较推荐到方式,之后会详细说到fixture的使用

setup_method和teardown_method我们来举个例子

    def setup_method(self):print("setup method")def teardown_method(self):print("teardown method")def test_01(self):print("True example")def test_02(self):print("True example02")

就比如上述代码,我们使用setup_method和teardown_method


我们看现象,在测试方法前我们调用setup_method方法,在测试方法后调用teardown_method方法,每个测试方法都会进行一次调用

setup_class和teardown_class我们同样举个例子看现象

    def test_01(self):print("True example01")def test_02(self):print("True example02")def setup_class(self):print("setup method")def teardown_class(self):print("teardown method")


我们看到在一个类内,只会调用一次setup/teardown方法

断言


断言能够帮我们检测程序的状态是否符合我们的预期,如果断言失败,那么python解释器会给我们抛出一个AssertionError异常,pytest中允许我们使用python中的断言语句来验证预期和值

条件必须是一个布尔表达式,错误信息选填
这里简单写几个示例

class Test01():#断言整数a=1b=2assert a==b#断言字符串s1="str"assert s1=="str"#断言列表expectList=[1,'aaa',3.21]realList=[1,'aaa',3.21]assert expectList==realList#断言元组expectTuple=(1,'aaa',3.21)realTuple=(1,'aaa',3.21)assert expectTuple==realTuple#断言字典expectDict={'a':1,'b':2,'c':3}realDict={'a':1,'b':2,'c':3}assert expectDict==realDict#断言集合expectSet={'a','b','c'}realSet={'a','b','c'}assert expectSet==realSet

在执行后,我们看到报错信息还是非常的明显的,会告诉我们那个断言错误了


这样我们可以利用断言来判断接口的返回值是否符合我们的预期

def test1():url = "http://jsonplaceholder.typicode.com/posts/1"r = requests.get(url=url)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"}print(r.json())assert r.json() == expect_dataassert r.json()['userId'] == 1

如果结果不符合预期就会断言失败

参数化


上述的代码,所有参数都是固定的,那么有没有一种方式可以像方法一样传参,让过程更加灵活可控呢?

我们可以用pytest内置的pytest.mark.parametrize来对测试函数的参数进行参数化
直接看代码

 @pytest.mark.parametrize("data",(1,2,3,4,5))def test_01(self,data):print("data: ",data)

  我们给方法设定一个参数data,然后通过pytest

  @pytest.mark.parametrize("data,result",[(3+5,8),(1+2,3)])def test_01(self,data,result):assert data==result

.mark.parametrize中第一个参数为要传的参数,第二个为参数的值

我们不止可以设定一个参数,也可以设置多个参数
代码如下:



此外除了可以在方法上使用这个参数化之外,在类上同样可以使用参数化

@pytest.mark.parametrize("data,result", [(3 + 5, 8), (1 + 2, 3)])
class Test01():def test_01(self, data, result):assert data == resultdef test_02(self, data, result):print(data,result)

现象如下,我们发现都会执行到

此时可能就会有疑问,当我们设定了同样的参数在类和方法上,会发生什么?可以来执行看下

@pytest.mark.parametrize("data,result", [(3 + 5, 8), (1 + 2, 3)])
class Test01():def test_01(self, data, result):assert data == result@pytest.mark.parametrize("data,result", [(1 + 5, 6), (2 + 2, 4)])def test_02(self, data, result):print(data,result)


上述告诉我们多次尝试对同一个参数进行重复参数化问题,解决办法也很简单
1.我们可以把类层和方法层参数合并到同一个层级
2.类层和方法层使用不同的参数名
除了可以手动指定参数外,我们也可以使用方法返回值来当作参数,只需要把手动指定参数的地方换成函数调用即可,类似于这样

注:当我们设定了参数化找不到对应参数时,是会报错的


fixture

用来提供测试函数所需要的资源或者上下文,有点类似于AOP面向切面编程的思想
以下是fixture的一些概念及使用场景
我们来看fixture的基础使用

class Test01():def fixture01(self):print("fixture1111")def test02(self):Test01.fixture01(self)print("test02")
class Test01():@pytest.fixture()def fixture01(self):print("fixture1111")def test02(self,fixture01):#    Test01.fixture01(self)print("test02")

我们看上述两个代码,第一种是在第二个方法中调用第一个方法
而第二种方法,是直接将函数名作为参数进行调用
我们看结果都是一样的

当然也可以在第一种的fixture方法上加上注解,也可以正常使用
那传到参数和方法中调用有什么区别呢?


除了基本使用fixture,也可以嵌套的使用fixture

class Test01():@pytest.fixture()def fixture01(self):return "01"@pytest.fixture()def est02(self,fixture01):#    Test01.fixture01(self)return fixture01def test03(self, est02):#    Test01.fixture01(self)print(est02)

与函数嵌套很类似

不止嵌套,我们同样可以在参数列表中调用多个fixture注解修饰的函数

class Test01():@pytest.fixture()def fixture01(self):return "01"@pytest.fixture()def est02(self,fixture01):#    Test01.fixture01(self)return "010203"def test03(self, fixture01,est02):#    Test01.fixture01(self)assert fixture01 in est02



fixture不止传参提供资源这一个用处,之前说过它可以用作于上下文
此时我们需要在代码中加上yield,这个主要是为了在我们运行测试时,确保它能够正确的自我清理,以便他不会干扰到其他的测试
我们使用yield而不是return,这样我们可以运行一些代码后,把对象返回给其他请求方法
但与return不同的是,该fixture的任何拆解代码要放在yield之后
一旦pytest确定了fixture,他会运行所有的fixture知道返回或者yield,然后执行下一个fixture重复此工作
测试完成后,pytest会逆向遍历fixture,对于每个yield后的fixture,运行yield语句之后的代码

class Test01():@pytest.fixture()def fixture01(self):print("start")yieldprint("stop")def test03(self, fixture01):#    Test01.fixture01(self)print("test03")

我们预期的表现是先打印对应方法中调用的fixture在yield前的代码,然后执行函数体,最后执行yield后的代码,看现象

带参数的fixture
看下这个注解的源码,我们看到有很多参数,但是我们上面在使用时,暂时没涉及到,这里来讲一下参数都有什么用


scope:
function:每个测试函数会调用fixture(也是默认的值)
class:在同一个测试类中共享这个fixture

class Test01():@pytest.fixture(scope="class")def fixture01(self):print("start")yieldprint("stop")def test05(self):#    Test01.fixture01(self)print("test03")def test03(self, fixture01):#    Test01.fixture01(self)print("test03")def test04(self):#    Test01.fixture01(self)print("test04")

按顺序先执行test05方法,因不涉及到fixture方法所以还没有调用,到了test03,先执行fixture方法中yield前的部分,等到class结束后执行yield后的部分
module:在一个文件里共享这个fixture
session:整个测试会话中共享这个fixture

autouse:默认参数为false,代表我们需要显示传入,才会调用,如果设置为true,就代表每个测试函数都会自动的调用fixture
params:用于参数化,fixture支持列表,每个参数都会让fixture执行一次,类似for循环
ids:与params搭配使用,为每个参数化实例指定标识符
类似这样

@pytest.fixture(params=[1, 2, 3],ids=["测试值1", "测试值2", "测试值3"]
)
def number(request):return request.param

name:用来给fixture方法设定一个名称,如果使用name,则在测试函数中需要使用这个名称来引用fixture


剩下的命令,下一篇再说

http://www.lryc.cn/news/614311.html

相关文章:

  • 微软XBOX游戏部门大裁员
  • QS菜单栏的安全与隐私Tile组件(GMS中的)加载逻辑
  • 使用 C# 通过 .NET 框架开发应用程序的安装与环境配置
  • Godot ------ 通过鼠标对节点进行操作
  • 僵尸进程、孤儿进程、进程优先级、/proc 文件系统、CRC 与网络溢出问题处理(实战 + 原理)
  • 强制用户更改WordPress密码的重要性及实现方法
  • Linux 内存管理之page folios
  • 电脑定时开关机终极指南
  • 静态路由主备切换
  • 2025产品经理接单经验分享与平台汇总
  • 腾讯云 CodeBuddy IDE:可以使用gpt5的ide
  • 操作系统-实验-进程
  • CVRF 是什么?微软弃用 MS 编号后,网络安全的下一个标准
  • 文件结构树的├、└、─ 符号
  • Java文件操作和IO
  • R语言代码加密(1)
  • 无人机航拍数据集|第9期 无人机风力电机表面损伤目标检测YOLO数据集2995张yolov11/yolov8/yolov5可训练
  • 【前端】纯代码实现Power BI自动化
  • 深入解析K-means聚类:从原理到调优实战
  • Milvus向量数据库版本升级
  • Agent 开发进阶路线:从基础功能到自主决策
  • [Python]代码随想录Day35[动态规划][背包问题]
  • ELK+Redis+Nginx多节点部署实战:从日志收集到可视化分析
  • Docker容器部署discuz论坛与线上商城
  • Centos7升级redis
  • springboot读取编译时传递的参数
  • Spring AI 系列之四十 - Spring AI Alibaba-集成百炼智能体
  • 用browse实现菜单功能的方法
  • 《在 Spring Boot 中安全使用 Qwen API-KEY:环境变量替代明文配置的最佳实践》
  • 一文可视化分析2025年6月计算机视觉顶刊IJCV前沿热点