关注

【接口自动化】05-轻量级框架封装:统一请求封装


一、前言

思考一个问题:你怎么去做一个接口自动化测试的实战?
首先,我们得有一个接口文档,本文以商城业务接口为例,从原生 requests 编写自动化用例,讲解接口关联处理,再完成请求工具类封装,适合新手掌握接口自动化完整实战流程。

一、纯使用requests模块进行接口测试

我们可以试一下,如果不做任何封装,纯使用requests模块进行接口测试,那么会怎么样?

方法:
我们拿到一个项目的接口文档的时候,他是有很多的接口的,你并不是盲目的去测,我们起初得先理清楚这个项目的核心业务逻辑。

那么你怎么去了解这个业务的逻辑呢?我们可以去跑一下这个项目,运行一下这个项目,访问一下这个项目。

比如我们的购物商城项目,商城项目的核心是需要把东西买下来呀,比如我选择一个商品,然后加入购物车,加入购物车之后,我能够在购物车中,加入购物车之后,我要能够在购物车中,选择这个商品然后进行结算,此时创建了一个订单,然后我们的订单就在我们的个人中心中就可以看到了:
1)注册
在这里插入图片描述
2)创建订单
在这里插入图片描述
3)个人中心有订单
在这里插入图片描述

拿到一个接口文档之后,里面是有很多个接口的,我们并不是所有接口都一个一个的测试,我们优先测试核心业务接口,如果没有一个合理的顺序的话,那么结果就是不太理想的。

1、核心业务流程

  • 项目访问
  • 用户登录
  • 查看商品(商品详情)
  • 加入购物车
  • 创建订单(订单确认)
  • 订单详情(订单列表、订单详情)

2、找到对应的接口

我们核心业务处理出来之后,我们就得去找这些核心的业务所对应的接口了。
我们找到对应的接口之后,怎么做?
答:核心看请求参数和响应结果

原因是我们需要对用例进行断言,所以你这一步就很关键了

3、对应的接口

本接口只是测试的例子,仅供大家参考,老铁们如果有自己的项目,那么就可以照着方法去使用即可。
我们的方法都是根据一个核心的业务流程去做的一个接口测试。

说明

请求示例

http://您的域名/shop/api.php?s=index/index&application=app&application_client_type=h5&token=token值&ajax=ajax

s=xxx
代表的是你哪一个接口(这些含义我们看需求文档自己分析即可)


公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用
application_client_typestringpc请求客户端
tokenstring用户token

请求应用详解 - application

参数值描述
web网页端
appAPP/小程序端

请求客户端详解 - application_client_type

参数值描述
pcPC网页端
h5手机H5端
ios苹果APP
android安卓APP
weixin微信小程序
alipay支付宝小程序
baidu百度小程序
toutiao头条/抖音小程序
qqQQ小程序

返回结构详解

字段描述
code状态码,0成功,负数失败
msg提示信息
data返回数据

code等于0表示成功,此时data可能为空;code为负数时msg为错误提示。


首页

简要描述

首页

接口版本

版本号制定人制定日期修订日期
1.0.0tan2026-06-15

请求URL

index/index

请求方式

POST

公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用、参考公共中详解
application_client_typestringpc请求客户端、参考公共中详解
tokenstringtoken

请求参数

返回示例

正确时返回

{
    "msg": "success",
    "code": 0,
    "data": {
        "navigation": [...],
        "banner_list": [...],
        "data_list": [...],
        "common_cart_total": 6,
        "plugins_limitedtimediscount_data": {...},
        "plugins_salerecords_data": {...}
    }
}

错误时返回

{
    "msg": "error",
    "code": -1,
    "data": []
}

返回参数说明 -> data

参数名是否必须类型描述
navigationarrayicon导航
banner_listarray轮播
data_listarray楼层数据
common_cart_totalint购物车总数
plugins_limitedtimediscount_dataarray限时秒杀 - 插件
plugins_salerecords_dataarray购买记录 - 插件

账号登录

简要描述

账号登录

接口版本

版本号制定人制定日期修订日期
1.0.0tan2026-06-15

请求URL

user/login

请求方式

POST

公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用、参考公共中详解
application_client_typestringpc请求客户端、参考公共中详解
tokenstringtoken

请求参数

参数名是否必须类型描述
accountsstring用户名/手机/邮箱
pwdstring密码
verifystring图片验证码
typestring登录类型

请求示例

{
    "accounts": "qqqqqq",
    "pwd": "xxx***xxx",
    "verify": "rib5",
    "type": "username"
}

返回示例

正确时返回

{
    "msg": "登录成功",
    "code": 0,
    "data": {
        "id": "1",
        "token": "6c3d302754fe2d850b14a6775953135b",
        "username": "qqqqqq",
        "nickname": "qqqqqq",
        "mobile": "13222222222",
        "email": "[email protected]",
        "avatar": "https://xxx.jpg",
        "integral": "4380",
        "...": "用户基础数据"
    }
}

错误时返回

{
    "msg": "error",
    "code": -1,
    "data": []
}

返回参数说明 -> data

用户基础数据

此处,有我们的token返回,那么这个token,我们只需要加入公共参数之后,我们的其他接口就可以访问了,这个逻辑我们又在接口测试工具,比如jmeter或者postman中介绍过。

商品详情

这个商品详情我们用来看我们需要下哪一些订单

简要描述

商品详情

接口版本

版本号制定人制定日期修订日期
1.0.0tan2026-06-15

请求URL

goods/detail

请求方式

POST

公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用、参考公共中详解
application_client_typestringpc请求客户端、参考公共中详解
tokenstringtoken

请求参数

参数名是否必须类型默认值描述
goods_idint商品id

请求示例

{
    "goods_id": "12"
}

返回示例

正确时返回

{
    "msg": "success",
    "code": 0,
    "data": {
        "goods": {...},
        "common_cart_total": 6,
        "buy_button": [...],
        "middle_tabs_nav": [...],
        "plugins_limitedtimediscount_data": {...},
        "plugins_salerecords_data": {...},
        "plugins_coupon_data": {...}
    }
}

错误时返回

{
    "msg": "error",
    "code": -1,
    "data": []
}

返回参数说明 -> data

参数名是否必须类型描述
goodsobject商品数据
common_cart_totalint购物车总数
buy_buttonarray购买操作按钮列表
middle_tabs_navarray顶部导航
plugins_limitedtimediscount_dataarray限时秒杀 - 插件
plugins_salerecords_dataarray购买记录 - 插件
plugins_coupon_dataarray优惠券 - 插件

加入购物车

找到对应的订单之后,就把他加入购物车

简要描述

商品加入购物车

接口版本

版本号制定人制定日期修订日期
1.0.0tan2026-06-15

请求URL

cart/save

请求方式

POST

公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用、参考公共中详解
application_client_typestringpc请求客户端、参考公共中详解
tokenstringtoken(需登录)

请求参数

参数名是否必须类型默认值描述
goods_idint商品id
specarray选择的规格
stockint数量

请求示例

{
    "goods_id": "2",
    "spec": [
        {
            "type": "套餐",
            "value": "套餐二"
        },
        {
            "type": "颜色",
            "value": "银色"
        },
        {
            "type": "容量",
            "value": "64G"
        }
    ],
    "stock": 2
}

返回示例

正确时返回

{
    "msg": "加入成功",
    "code": 0,
    "data": 6
}

错误时返回

{
    "msg": "error",
    "code": -1,
    "data": []
}

返回参数说明 -> data

参数名是否必须类型描述
dataint当前购物车商品总数

订单确认

我们在购物车中开始对订单进行下单,也就是创建订单

简要描述

订单确认

接口版本

版本号制定人制定日期修订日期
1.0.0tan2026-06-15

请求URL

buy/index

请求方式

POST

公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用、参考公共中详解
application_client_typestringpc请求客户端、参考公共中详解
tokenstringtoken(需登录)

请求参数

参数名是否必须类型默认值描述
buy_typestring类型:cart购物车、goods直接购买
address_idint0地址id
payment_idint0支付方式id
site_modelint0订单模式
brand_idint品牌id
is_pointsint0是否使用积分
idsstring购物车购买id(buy_type=cart时传)
goods_idint直接购买商品id(buy_type=goods时传)
specarray直接购买商品规格
stockint直接购买商品数量

请求示例

{
    "buy_type": "cart",
    "ids": "189",
    "address_id": 0,
    "payment_id": 0,
    "site_model": 0,
    "is_points": 0
}

返回示例

正确时返回

{
    "msg": "success",
    "code": 0,
    "data": {
        "goods_list": [...],
        "payment_list": [...],
        "base": {...},
        "common_site_type": 2,
        "plugins_points_data": {...},
        "plugins_coupon_data": [...]
    }
}

错误时返回

{
    "msg": "error",
    "code": -1,
    "data": []
}

返回参数说明 -> data

参数名是否必须类型描述
common_site_typeint站点类型
basearray基础信息
goods_listarray商品列表
payment_listarray支付方式
plugins_points_dataarray积分商城 - 插件
plugins_coupon_dataarray优惠券 - 插件

按理来说,你创建完订单之后,后续会返回一个订单id,这个id用于到时候查询订单详情用到,但是我们此处的接口文档并未返回订单id。

而订单id,在我们的订单列表中返回的,我们的项目不排除也有这种情况,所以我们根据前面的用例得出订单列表和这个接口,我们需要去考虑

订单列表

简要描述

订单列表

接口版本

版本号制定人制定日期修订日期
1.0.0tan2026-06-15

请求URL

order/index

请求方式

POST

公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用、参考公共中详解
application_client_typestringpc请求客户端、参考公共中详解
tokenstringtoken(需登录)

请求参数

参数名是否必须类型默认值描述
pageint1分页
keywordsstring搜索关键字
statusint-1订单状态(-1所有)
is_moreint1是否更多参数

请求示例

{
    "page": 1,
    "keywords": "",
    "status": "-1",
    "is_more": 1
}

返回示例

正确时返回

{
    "msg": "success",
    "code": 0,
    "data": {
        "total": 1,
        "page_total": 1,
        "data": [...],
        "payment_list": [...]
    }
}

错误时返回

{
    "msg": "error",
    "code": -1,
    "data": []
}

返回参数说明 -> data

参数名是否必须类型描述
page_totalint分页总数
totalint数据总数
dataarray订单数据列表
payment_listarray支付方式列表

我们的订单列表中会返回所有订单,那么此时我们就只能根据他返回的列表,从中找到最新的订单id,这个订单id就是我们刚刚创建的,再根据订单id,得到我们的订单详情。

订单详情

简要描述

订单详情

接口版本

版本号制定人制定日期修订日期
1.0.0tan2026-06-15

请求URL

order/detail

请求方式

POST

公共参数

参数名是否必须类型默认值描述
applicationstringweb请求应用、参考公共中详解
application_client_typestringpc请求客户端、参考公共中详解
tokenstringtoken(需登录)

请求参数

参数名是否必须类型默认值描述
idint订单id

请求示例

{
    "id": "12"
}

返回示例

正确时返回

{
    "msg": "success",
    "code": 0,
    "data": {
        "data": {...},
        "site_fictitious": {...}
    }
}

错误时返回

{
    "msg": "error",
    "code": -1,
    "data": []
}

返回参数说明 -> data

参数名是否必须类型描述
dataobject订单数据
site_fictitiousarray虚拟订单(虚拟信息)

4、接口自动化代码

编写测试用例,我们可以为每一个接口去创建一个测试用例,也可以为一个接口创建多个测试用例,总之,接口和用例之间是多对多的,所以你就自己看着创建就行了。

那么接下来就一步一步的跟着做就可以的实现一份接口自动化测试

访问首页

首先回顾我们之前的pytest框架的知识,我们启动这个框架的话,我们使用的是启动类,所以我们就先写一个启动类:

# 导入我们的pytest框架 到时候为我们去做测试
import pytest
import os # 导入我们的os可以执行系统命令,好比就在我们的cmd操作一样

# 启动pytest
pytest.main()
os.system("allure generate ./temps -o ./reports --clean") # 根据数据生成Allure报告

那么我们pytest启动的时候,他会去读取我们的配置文件,这个配置文件的中我们可以写pytest的启动参数(这个我们在之前的pytest的学习中介绍过)

# 节[pytest]:使用pytest的时候会读取这个配置文件
[pytest]
# -vs: -v代表的是我们执行结果会输出比较详细的信息 -s代表的是允许我们的输入输出执行
# --alluredir=./temps : allure的执行数据的目录
# --clean-alluredir: 将allure的执行数据的目录中旧数据删除
addopts = -vs --alluredir=./temps --clean-alluredir


接下来,我们就新建一个py文件,用于做测试,注意这个文件的文件名得是test_开头,只有这样,我们的pytest框架才找得到(约定大于配置),最后我们后续写代码的时候具体的细节将在注释中体现:
在这里插入图片描述
代码:

# 首先我们导入我们的requests模块进行接口测试
import requests

# 创建出我们的一个会话
session = requests.session()

# 接下里我们就写测试用例,然后构造请求 对结果断言等等

# 测试用例1:访问首页
def test_index_index(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"
    # 查询字符串参数我们后续在requests中使用的是params这个名称
    params = {
        "s":"index/index",
        "application":"app", # 假如我们测app端(注意测试思维)
        "application_client_type":"android" # app端的安卓类型
    }# 多个就以键值对方式来弄吧

    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数
        params=params
    )
    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0


结果:
在这里插入图片描述

这里有一个疑问:assert resp.json()[‘code’] == 0是什么意思?

  • assert 是断言
  • resp.json()是由于接口文档中说你返回的是JSON字符串,那么我为了能够拿到你里面的数据,我就得将你这个JSON字符串变为JSON对象,此时就得需要使用resp.json(),不过只有返回的是JSON字符串,才可以使用json()方法
  • resp.json()[‘code’]是我们json对象中根据key获取他对应的value值,这个我们在Python语法中字典的读取中说过。

还有一个疑问:你断言就断言,凭什么说:assert resp.json()[‘code’] == 0?
你为什么说返回中中的code属性如果为0就说明成功呢?
这个不是我们凭空去写的,是我们接口文档约定好的,看我们之前的接口文档:

返回结构详解

字段描述
code状态码,0成功,负数失败
msg提示信息
data返回数据

code等于0表示成功,此时data可能为空;code为负数时msg为错误提示。


账户登录

接口测试是很简单的,你只要构造出请求,并对响应断言即可
那么怎么构造请求:都是请求方法、请求路径、参数、请求头等等
构造哪一些请求、如果断言,我们都得分析接口文档
首先分析我们的接口文档

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
代码:
这个代码怎么写?我们不用一个一个的敲,接口测试中,我们的接口和接口之间用例是有很大的相似性的,我们上述写了第一个用例之后,那么此处我们就直接复制上述的用例来修改就行了

# 测试用例2:账号登录
def test_user_login(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "user/login",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android" # app端的安卓类型
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
            "accounts": "tan", # 用户名
            "pwd": "123456", # 密码
            # "verify": "rib5", # 验证码(可不需要)
            "type": "username" # 保持默认
    }

    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )
    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0

结果:
在这里插入图片描述

补充:我们所有的代码都是围绕着接口文档写的,总之,你记住,如果代码看不懂,那么可先理解清楚我们的接口文档,我们只是将接口文档的要求,使用代码表述出来实现而已。
在这里插入图片描述


商品详情

# 测试用例3:商品详情
def test_goods_detail(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "goods/detail",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android" # app端的安卓类型
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
        "goods_id": "12" # 这个id我们现在先把他写死
    }

    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )
    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0

结果:
在这里插入图片描述

在这里插入图片描述

注意:我们断言不是将所有的数据都加到里面去,我们只加我们能够控制的,对于不能控制的,我们就使用手工测试


加入购物车

# 测试用例4:加入购物车
def test_cart_save(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "cart/save",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android" # app端的安卓类型
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
        "goods_id": "2",
        "stock": 2
    }

    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )

    # 我们可以输出响应,方便后续结果调试等等
    print(resp.json())

    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0

在这里插入图片描述
接下来我们看结果:
在这里插入图片描述
这个是一个鉴权逻辑,我们的鉴权逻辑是:你登录之后我会给你一个token,那么后续你去访问其他接口的时候,你就得加上这个token,此处我们失败的原因就是没有加token:
在这里插入图片描述
那么这个token来自哪里?这个token是登录之后,返回的,所以此处就涉及到了接口关联:登录接口返回的token值,作为后续我们的其他接口的参数,那么我们的处理也很简单,就是直接使用json提取器提取我们的返回值,存到变量中,然后我们后续所需接口就直接拿就行了👇

在这里插入图片描述

下一步:注意很关键,你当前这个token是一个局部变量,其他函数访问不了,所以务必声明为全局变量:
在这里插入图片描述

登录接口测试用例的完整代码:

# 测试用例2:账号登录
def test_user_login(): # 名字是test_接口名字等等

    # 将token声明为全局变量
    global token


    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "user/login",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android" # app端的安卓类型
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
            "accounts": "tan", # 用户名
            "pwd": "123456", # 密码
            # "verify": "rib5", # 验证码(可不需要)
            "type": "username" # 保持默认
    }

    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )
    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    # 注意:我们断言不是将所有的数据都加到里面去,我们只加我们能够控制的,对于不能控制的,我们就使用手工测试
    assert resp.json()['code'] == 0


    # 我们断言成功之后,就会提取返回值中的token值:下面是字典获取值的方法
    token = resp.json()['data']['token']

于是我们在加入购物车中,带上token👇
在这里插入图片描述

# 测试用例4:加入购物车
def test_cart_save(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "cart/save",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android",# app端的安卓类型
        "token": token #加入token
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
        "goods_id": "2",
        "stock": 2
    }

    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )

    # 我们可以输出响应,方便后续结果调试等等
    print(resp.json())

    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0

结果:
在这里插入图片描述

订单确认

# 测试用例5:确认订单
def test_buy_index(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "buy/index",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android",# app端的安卓类型
        "token": token #加入token
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
        "buy_type": "goods", # 直接下单
        "goods_id": "12",
        "spec": [], # 购买规格
        "stock": 1 # 购买数量
    }
    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )

    # 如果断言成功,那么就打印提示信息,如果断言失败,就返回响应

    res_data = resp.json()
    if res_data["code"] != 0:
        print("接口失败响应:", res_data)
        # 失败直接断言终止,不会执行下面的成功打印
        assert False, f"订单接口异常,错误码:{res_data['code']}"
    # 走到这里说明code一定等于0
    print("确定订单成功~")


在这里插入图片描述

结果:
在这里插入图片描述

订单列表

我们获取订单列表,主要是里面会返回最新的订单id,此时我们就可以根据这个id查询该订单的详情了。

# 测试用例6:订单列表
# @pytest.mark.skip # 先暂时跳过这个测试用例
def test_order_index(): # 名字是test_接口名字等等

    # 取出订单id的目的是后续订单详情需要用到,所以此处给他标记为全局
    global order_id

    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "order/index",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android",# app端的安卓类型
        "token": token
    }# 多个就以键值对方式来弄吧

    # 订单列表中没有固定的json参数


    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
    )

    # print(resp.json())

    # 我们将最新的订单id存起来(最新的订单id是在我们的第一行的)
    order_id = resp.json()["data"]["data"][0]["id"]

    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0

在这里插入图片描述
在这里插入图片描述
结果
在这里插入图片描述

补充:如果你写的这个测试用例还不想执行,那么你是可以使用装饰器来修饰的让他先暂时跳过
在这里插入图片描述

订单详情

订单列表中获取到的最新订单id,作为订单详情的参数

# 测试用例7:订单详情
def test_order_detail(): # 名字是test_接口名字等等

    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "order/detail",
        "application": "app", # 假如我们测app端(注意测试思维)
        "application_client_type": "android",# app端的安卓类型
        "token": token
    }# 多个就以键值对方式来弄吧

    # json参数

    json = {
        "id": order_id
    }


    # 开始使用框架发送我们的请求:这种函数传参,我们叫做关键字传参
    resp = session.request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        json=json
    )

    # print(resp.json())

    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0
    # 继续断言,你返回的结果中,id一定是参数中传递的order_id
    assert resp.json()["data"]["data"]["id"] == order_id

在这里插入图片描述


5、分析

  1. 首先我们上述的用例虽然多,代码虽然长,但是用例它的执行逻辑是比较相似的,基本上都是设置请求的四要素。发送请求得到响应,然后对响应进行断言,提取变量等等。
  2. 我们断言的内容和形式是有多种的,比如相等断言,对数据加工后断言,还有通用断言。
  3. 我们每一个用例的代码都是高度相似的,比如我们封装请求部分基本都是通用的。

既然有很多相似性的代码,那我们就不必去大量的编写重复代码,我们就对代码进行封装👇

6、接口自动化测试封装

在封装之前,我们需要了解两个概念,你这个框架封装的程度是什么?然后你这个框架封装的目的又是什么?
1.框架封装程度

  • 接口自动化的轻量级封装:没有彻底的封装,就是做一个基本的冗余性代码的封装
  • 接口自动化的零代码封装:极限性的封装(我们后续会介绍)

2.框架封装的目的

  • 简化用例的编写
  • 标准化用例的格式
  • 统一化框架的用法(如果你是一个人去做接口自动化测试,那你封不封装都无所谓,但如果你是一个团队的话,那就是封装的话,我们可以统一管理使用)

同时,我们封装框架的期望就是,我们只需要一个人搭建和维护框架即可,其他人专注用例的设计和执行。

接下来我们就开始进行轻量级封装了👇

二、接口自动化轻量级封装:统一请求的封装

1、统一请求封装的目的

  1. 统一请求封装有什么好处呢?我们的前面代码相似性极高,我们可以把相似性代码封装起来,方便复用去除重复、冗余的代码
  2. 我们可以设置统一的公共参数、统一的文件处理、统一的日志监控、统一的异常处理、统一的用例校验,一旦统一了,那么你维护代码的成本是很低的。
  3. 自动使用同一个Session,好处就是实现参数共享和cookie自动管理

2、统一请求封装的代码实现

这个封装统一请求,那我们对框架的封装,我们不是写测试用例,而是封装一个文件,我们将这个和封装的代码写在公共的目录下👇
在这里插入图片描述
统一请求封装的代码:
首先导入我们的requests模块
我们创建一个类,这几个类里面去封装我们的代码
将Session、公共参数变为类变量

封装一个请求的方法,参数使用不定长关键字参数,因为构造请求的时候,我们参数的个数是不确定的。

import requests

class RequestUtil:
    # 首先将session做为类对象,供给大家共用
    sess = requests.session()

    # 创建一个参数 用于共用公共参数
    params = {
        "application": "app",
        "application_client_type": "android",
    }

    # 创建一个方法构造请求
    def send_request(self, **kwargs): # 使用不定长的关键字参数
        # 完善动态参数
        if "params" in kwargs:
            # 不存在就添加,存在就是修改
            kwargs["params"].update(self.params)

        # 发送请求
        resp = self.sess.request(**kwargs)

        return resp

在这里插入图片描述
在这里插入图片描述

3、重构自动化接口测试代码

# 导入重构
from commons.request_util import RequestUtil



# 测试用例1:访问首页
def test_index_index(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"
    params = {
        "s": "index/index",
        # 将公共参数去除(重构)
    }

    # 调用处重构
    # RequestUtil()实例化对象
    resp = RequestUtil().send_request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数
        params=params
    )
    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0


# 测试用例2:账号登录
def test_user_login(): # 名字是test_接口名字等等

    # 将token声明为全局变量
    # global token 此处不再使用token为全局


    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    params = {
        "s": "user/login",
        # 公共参数已封装(重构)
    }

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
            "accounts": "tan", # 用户名
            "pwd": "123456", # 密码
            # "verify": "rib5", # 验证码(可不需要)
            "type": "username" # 保持默认
    }

    # 构造请求重构,调用我们封装的
    resp = RequestUtil().send_request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )
    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    # 注意:我们断言不是将所有的数据都加到里面去,我们只加我们能够控制的,对于不能控制的,我们就使用手工测试
    assert resp.json()['code'] == 0


    # 不再使用这个方法
    # token = resp.json()['data']['token']
    # 返回成功之后,我们就将token存入到类变量中(后面笔记会介绍)
    RequestUtil.params["token"] = resp.json()['data']['token']


# 测试用例3:商品详情
def test_goods_detail(): # 名字是test_接口名字等等

    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "goods/detail",
        # 公共参数已封装(重构)
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
        "goods_id": "12" # 这个id我们现在先把他写死
    }

    # 构造请求重构,调用我们封装的
    resp = RequestUtil().send_request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )
    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0



# 测试用例4:加入购物车
def test_cart_save(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    # 公共参数
    params = {
        "s": "cart/save",
        # 公共参数已封装(重构)
    }# 多个就以键值对方式来弄吧

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
        "goods_id": "12",
        "stock": 2
    }

    # 构造请求重构,调用我们封装的
    resp = RequestUtil().send_request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )

    # 我们可以输出响应,方便后续结果调试等等
    print(resp.json())

    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0



# 测试用例5:确认订单
def test_buy_index(): # 名字是test_接口名字等等
    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    params = {
        "s": "buy/index",
        # 公共参数已封装(重构)
    }

    # 这个接口需要的json参数: 每个参数填什么,我们看接口文档的要求
    json = {
        "buy_type": "goods", # 直接下单
        "goods_id": "12",
        "spec": [], # 购买规格
        "stock": 1 # 购买数量
    }
    # 构造请求重构,调用我们封装的
    resp = RequestUtil().send_request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        # json参数
        json=json
    )

    # 如果断言成功,那么就打印提示信息,如果断言失败,就返回响应

    res_data = resp.json()
    if res_data["code"] != 0:
        print("接口失败响应:", res_data)
        # 失败直接断言终止,不会执行下面的成功打印
        assert False, f"订单接口异常,错误码:{res_data['code']}"
    # 走到这里说明code一定等于0
    print("确定订单成功~")



# 测试用例6:订单列表
def test_order_index(): # 名字是test_接口名字等等

    # 取出订单id的目的是后续订单详情需要用到,所以此处给他标记为全局
    global order_id

    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    params = {
        "s": "order/index",
        # 公共参数已封装(重构)
    }

    # 订单列表中没有固定的json参数

    # 构造请求重构,调用我们封装的
    resp = RequestUtil().send_request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
    )

    # print(resp.json())

    # 我们将最新的订单id存起来(最新的订单id是在我们的第一行的)
    order_id = resp.json()["data"]["data"][0]["id"]

    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0



# 测试用例7:订单详情
def test_order_detail(): # 名字是test_接口名字等等

    # 构造请求:必须严格按照接口文档去构造
    # 请求方法、请求路径、请求参数、响应(断言)
    method = "post"
    url = "http://116.62.63.211/shop/api.php"

    params = {
        "s": "order/detail",
        # 公共参数已封装(重构)
    }

    # json参数

    json = {
        "id": order_id
    }

    # 构造请求重构,调用我们封装的
    resp = RequestUtil().send_request(
        # 请求方法
        method=method,
        # 请求路径
        url=url,
        # 请求参数(公共参数)
        params=params,
        json=json
    )

    # print(resp.json())

    # 请求完毕之后,他会有返回值,那么此时我们就可以断言
    assert resp.json()['code'] == 0
    # 继续断言,你返回的结果中,id一定是参数中传递的order_id
    assert resp.json()["data"]["data"]["id"] == order_id

结果:
在这里插入图片描述

调用方:
在这里插入图片描述

4、解释

1)dict.update() 完整详解

在这里插入图片描述

基础语法规则

语法:目标字典.update(待合并字典)
作用:把「待合并字典」里所有键值对,合并到「目标字典」中
两条核心判定逻辑:

  1. 如果目标字典 已有 和待合并字典相同的key:目标字典该key的值,会被待合并字典的值直接覆盖
  2. 如果目标字典 没有 待合并字典里的某个key:直接把这条key-value新增到目标字典
    补充:update不会清空目标字典原有内容,只做新增/覆盖操作

极简示例演示覆盖&新增

定义两个字典
dict_a = {“s”: “index/index”} # 目标字典
dict_b = {“application”: “app”, “name”: “test”} # 待合并字典
dict_a.update(dict_b)
print(dict_a)
输出结果:{“s”: “index/index”, “application”: “app”, “name”: “test”}
解释:dict_a没有application、name两个key → 全部新增

再来一组存在重复key的例子
dict_a = {“s”: “index/index”, “application”: “pc”}
dict_b = {“application”: “app”, “client”: “android”}
dict_a.update(dict_b)
print(dict_a)
输出结果:{“s”: “index/index”, “application”: “app”, “client”: “android”}
解释:

  1. key=application 两边都存在 → dict_a的值被dict_b覆盖(pc → app)
  2. key=client dict_a不存在 → 直接新增

套入当前代码逐行拆解

你的合并代码:kwargs[“params”].update(self.params)
拆分两个对象:

  1. 目标字典:kwargs[“params”] 用户调用传入的业务参数字典
  2. 待合并字典:self.params 类内固定公共参数 {“application”:“app”,“application_client_type”:“android”}

场景1:业务参数无重复key(日常场景)
调用传入 params={“s”: “index/index”}
执行update前目标字典:{“s”: “index/index”}
执行update后:
{“s”: “index/index”, “application”: “app”, “application_client_type”: “android”}
逻辑:公共参数的两个key业务字典都没有 → 全部新增,不会覆盖原有s参数

场景2:业务参数和公共参数存在同名key
调用传入 params={“s”: “index/index”, “application”: “web”}
执行update前目标字典:{“s”: “index/index”, “application”: “web”}
执行update后:
{“s”: “index/index”, “application”: “app”, “application_client_type”: “android”}
逻辑:key=application两边都存在,公共参数的值覆盖业务传入的值

2)对于token,我们封装在公共参数中

在这里插入图片描述
那么后续的所有请求,我们的公共参数中都会有token的
在这里插入图片描述


最后,我们本期的统一接口请求封装就告一段落,我们的封装还有很多,比如日志、数据驱动等等,更多的内容我们后续会一一介绍。

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2301_81982617/article/details/161974493

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--