文章目录:
- 一、前言
- 一、纯使用requests模块进行接口测试
- 二、接口自动化轻量级封装:统一请求的封装
-
- 1、统一请求封装的目的
- 2、统一请求封装的代码实现
- 3、重构自动化接口测试代码
- 4、解释
-
- [1)dict.update() 完整详解](#1)dict.update() 完整详解)
- 2)对于token,我们封装在公共参数中
一、前言
思考一个问题:你怎么去做一个接口自动化测试的实战?
首先,我们得有一个接口文档,本文以商城业务接口为例,从原生 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=ajaxs=xxx
代表的是你哪一个接口(这些含义我们看需求文档自己分析即可)
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用 application_client_type 是 string pc 请求客户端 token 否 string 用户token
请求应用详解 - application
参数值 描述 web 网页端 app APP/小程序端
请求客户端详解 - application_client_type
参数值 描述 pc PC网页端 h5 手机H5端 ios 苹果APP android 安卓APP weixin 微信小程序 alipay 支付宝小程序 baidu 百度小程序 toutiao 头条/抖音小程序 QQ小程序
返回结构详解
字段 描述 code 状态码,0成功,负数失败 msg 提示信息 data 返回数据 code等于0表示成功,此时data可能为空;code为负数时msg为错误提示。
首页
简要描述
首页
接口版本
版本号 制定人 制定日期 修订日期 1.0.0 tan 2026-06-15 请求URL
index/index
请求方式
POST
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用、参考公共中详解 application_client_type 是 string pc 请求客户端、参考公共中详解 token 否 string token 请求参数
无
返回示例
正确时返回
json{ "msg": "success", "code": 0, "data": { "navigation": [...], "banner_list": [...], "data_list": [...], "common_cart_total": 6, "plugins_limitedtimediscount_data": {...}, "plugins_salerecords_data": {...} } }错误时返回
json{ "msg": "error", "code": -1, "data": [] }返回参数说明 -> data
参数名 是否必须 类型 描述 navigation 是 array icon导航 banner_list 是 array 轮播 data_list 是 array 楼层数据 common_cart_total 是 int 购物车总数 plugins_limitedtimediscount_data 是 array 限时秒杀 - 插件 plugins_salerecords_data 是 array 购买记录 - 插件
账号登录
简要描述
账号登录
接口版本
版本号 制定人 制定日期 修订日期 1.0.0 tan 2026-06-15 请求URL
user/login
请求方式
POST
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用、参考公共中详解 application_client_type 是 string pc 请求客户端、参考公共中详解 token 否 string token 请求参数
参数名 是否必须 类型 描述 accounts 是 string 用户名/手机/邮箱 pwd 是 string 密码 verify 否 string 图片验证码 type 是 string 登录类型 请求示例
json{ "accounts": "qqqqqq", "pwd": "xxx***xxx", "verify": "rib5", "type": "username" }返回示例
正确时返回
json{ "msg": "登录成功", "code": 0, "data": { "id": "1", "token": "6c3d302754fe2d850b14a6775953135b", "username": "qqqqqq", "nickname": "qqqqqq", "mobile": "13222222222", "email": "xxx@qq.com", "avatar": "https://xxx.jpg", "integral": "4380", "...": "用户基础数据" } }错误时返回
json{ "msg": "error", "code": -1, "data": [] }返回参数说明 -> data
用户基础数据
此处,有我们的token返回,那么这个token,我们只需要加入公共参数之后,我们的其他接口就可以访问了,这个逻辑我们又在接口测试工具,比如jmeter或者postman中介绍过。
商品详情
这个商品详情我们用来看我们需要下哪一些订单
简要描述
商品详情
接口版本
版本号 制定人 制定日期 修订日期 1.0.0 tan 2026-06-15 请求URL
goods/detail
请求方式
POST
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用、参考公共中详解 application_client_type 是 string pc 请求客户端、参考公共中详解 token 否 string token 请求参数
参数名 是否必须 类型 默认值 描述 goods_id 是 int 商品id 请求示例
json{ "goods_id": "12" }返回示例
正确时返回
json{ "msg": "success", "code": 0, "data": { "goods": {...}, "common_cart_total": 6, "buy_button": [...], "middle_tabs_nav": [...], "plugins_limitedtimediscount_data": {...}, "plugins_salerecords_data": {...}, "plugins_coupon_data": {...} } }错误时返回
json{ "msg": "error", "code": -1, "data": [] }返回参数说明 -> data
参数名 是否必须 类型 描述 goods 是 object 商品数据 common_cart_total 是 int 购物车总数 buy_button 是 array 购买操作按钮列表 middle_tabs_nav 是 array 顶部导航 plugins_limitedtimediscount_data 是 array 限时秒杀 - 插件 plugins_salerecords_data 是 array 购买记录 - 插件 plugins_coupon_data 是 array 优惠券 - 插件
加入购物车
找到对应的订单之后,就把他加入购物车
简要描述
商品加入购物车
接口版本
版本号 制定人 制定日期 修订日期 1.0.0 tan 2026-06-15 请求URL
cart/save
请求方式
POST
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用、参考公共中详解 application_client_type 是 string pc 请求客户端、参考公共中详解 token 是 string token(需登录) 请求参数
参数名 是否必须 类型 默认值 描述 goods_id 是 int 商品id spec 否 array 选择的规格 stock 是 int 数量 请求示例
json{ "goods_id": "2", "spec": [ { "type": "套餐", "value": "套餐二" }, { "type": "颜色", "value": "银色" }, { "type": "容量", "value": "64G" } ], "stock": 2 }返回示例
正确时返回
json{ "msg": "加入成功", "code": 0, "data": 6 }错误时返回
json{ "msg": "error", "code": -1, "data": [] }返回参数说明 -> data
参数名 是否必须 类型 描述 data 是 int 当前购物车商品总数
订单确认
我们在购物车中开始对订单进行下单,也就是创建订单
简要描述
订单确认
接口版本
版本号 制定人 制定日期 修订日期 1.0.0 tan 2026-06-15 请求URL
buy/index
请求方式
POST
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用、参考公共中详解 application_client_type 是 string pc 请求客户端、参考公共中详解 token 是 string token(需登录) 请求参数
参数名 是否必须 类型 默认值 描述 buy_type 否 string 类型:cart购物车、goods直接购买 address_id 否 int 0 地址id payment_id 否 int 0 支付方式id site_model 否 int 0 订单模式 brand_id 否 int 品牌id is_points 否 int 0 是否使用积分 ids 否 string 购物车购买id(buy_type=cart时传) goods_id 否 int 直接购买商品id(buy_type=goods时传) spec 否 array 直接购买商品规格 stock 否 int 直接购买商品数量 请求示例
json{ "buy_type": "cart", "ids": "189", "address_id": 0, "payment_id": 0, "site_model": 0, "is_points": 0 }返回示例
正确时返回
json{ "msg": "success", "code": 0, "data": { "goods_list": [...], "payment_list": [...], "base": {...}, "common_site_type": 2, "plugins_points_data": {...}, "plugins_coupon_data": [...] } }错误时返回
json{ "msg": "error", "code": -1, "data": [] }返回参数说明 -> data
参数名 是否必须 类型 描述 common_site_type 是 int 站点类型 base 是 array 基础信息 goods_list 是 array 商品列表 payment_list 是 array 支付方式 plugins_points_data 是 array 积分商城 - 插件 plugins_coupon_data 是 array 优惠券 - 插件
按理来说,你创建完订单之后,后续会返回一个订单id,这个id用于到时候查询订单详情用到,但是我们此处的接口文档并未返回订单id。
而订单id,在我们的订单列表中返回的,我们的项目不排除也有这种情况,所以我们根据前面的用例得出订单列表和这个接口,我们需要去考虑
订单列表
简要描述
订单列表
接口版本
版本号 制定人 制定日期 修订日期 1.0.0 tan 2026-06-15 请求URL
order/index
请求方式
POST
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用、参考公共中详解 application_client_type 是 string pc 请求客户端、参考公共中详解 token 是 string token(需登录) 请求参数
参数名 是否必须 类型 默认值 描述 page 否 int 1 分页 keywords 否 string 搜索关键字 status 否 int -1 订单状态(-1所有) is_more 否 int 1 是否更多参数 请求示例
json{ "page": 1, "keywords": "", "status": "-1", "is_more": 1 }返回示例
正确时返回
json{ "msg": "success", "code": 0, "data": { "total": 1, "page_total": 1, "data": [...], "payment_list": [...] } }错误时返回
json{ "msg": "error", "code": -1, "data": [] }返回参数说明 -> data
参数名 是否必须 类型 描述 page_total 是 int 分页总数 total 是 int 数据总数 data 是 array 订单数据列表 payment_list 是 array 支付方式列表
我们的订单列表中会返回所有订单,那么此时我们就只能根据他返回的列表,从中找到最新的订单id,这个订单id就是我们刚刚创建的,再根据订单id,得到我们的订单详情。
订单详情
简要描述
订单详情
接口版本
版本号 制定人 制定日期 修订日期 1.0.0 tan 2026-06-15 请求URL
order/detail
请求方式
POST
公共参数
参数名 是否必须 类型 默认值 描述 application 是 string web 请求应用、参考公共中详解 application_client_type 是 string pc 请求客户端、参考公共中详解 token 是 string token(需登录) 请求参数
参数名 是否必须 类型 默认值 描述 id 是 int 订单id 请求示例
json{ "id": "12" }返回示例
正确时返回
json{ "msg": "success", "code": 0, "data": { "data": {...}, "site_fictitious": {...} } }错误时返回
json{ "msg": "error", "code": -1, "data": [] }返回参数说明 -> data
参数名 是否必须 类型 描述 data 是 object 订单数据 site_fictitious 否 array 虚拟订单(虚拟信息)
4、接口自动化代码
编写测试用例,我们可以为每一个接口去创建一个测试用例,也可以为一个接口创建多个测试用例,总之,接口和用例之间是多对多的,所以你就自己看着创建就行了。
那么接下来就一步一步的跟着做就可以的实现一份接口自动化测试
访问首页
首先回顾我们之前的pytest框架的知识,我们启动这个框架的话,我们使用的是启动类,所以我们就先写一个启动类:
js
# 导入我们的pytest框架 到时候为我们去做测试
import pytest
import os # 导入我们的os可以执行系统命令,好比就在我们的cmd操作一样
# 启动pytest
pytest.main()
os.system("allure generate ./temps -o ./reports --clean") # 根据数据生成Allure报告
那么我们pytest启动的时候,他会去读取我们的配置文件,这个配置文件的中我们可以写pytest的启动参数(这个我们在之前的pytest的学习中介绍过)
py
# 节[pytest]:使用pytest的时候会读取这个配置文件
[pytest]
# -vs: -v代表的是我们执行结果会输出比较详细的信息 -s代表的是允许我们的输入输出执行
# --alluredir=./temps : allure的执行数据的目录
# --clean-alluredir: 将allure的执行数据的目录中旧数据删除
addopts = -vs --alluredir=./temps --clean-alluredir
接下来,我们就新建一个py文件,用于做测试,注意这个文件的文件名得是test_开头,只有这样,我们的pytest框架才找得到(约定大于配置),最后我们后续写代码的时候具体的细节将在注释中体现:

代码:
py
# 首先我们导入我们的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为错误提示。
账户登录
接口测试是很简单的,你只要构造出请求,并对响应断言即可
那么怎么构造请求:都是请求方法、请求路径、参数、请求头等等
构造哪一些请求、如果断言,我们都得分析接口文档
首先分析我们的接口文档



代码:
这个代码怎么写?我们不用一个一个的敲,接口测试中,我们的接口和接口之间用例是有很大的相似性的,我们上述写了第一个用例之后,那么此处我们就直接复制上述的用例来修改就行了
py
# 测试用例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
结果:

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

商品详情
py
# 测试用例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
结果:


注意:我们断言不是将所有的数据都加到里面去,我们只加我们能够控制的,对于不能控制的,我们就使用手工测试
加入购物车
py
# 测试用例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是一个局部变量,其他函数访问不了,所以务必声明为全局变量:

登录接口测试用例的完整代码:
py
# 测试用例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👇

py
# 测试用例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
结果:

订单确认
py
# 测试用例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查询该订单的详情了。
py
# 测试用例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,作为订单详情的参数
py
# 测试用例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、分析
- 首先我们上述的用例虽然多,代码虽然长,但是用例它的执行逻辑是比较相似的,基本上都是设置请求的四要素。发送请求得到响应,然后对响应进行断言,提取变量等等。
- 我们断言的内容和形式是有多种的,比如相等断言,对数据加工后断言,还有通用断言。
- 我们每一个用例的代码都是高度相似的,比如我们封装请求部分基本都是通用的。
既然有很多相似性的代码,那我们就不必去大量的编写重复代码,我们就对代码进行封装👇
6、接口自动化测试封装
在封装之前,我们需要了解两个概念,你这个框架封装的程度是什么?然后你这个框架封装的目的又是什么?
1.框架封装程度
- 接口自动化的轻量级封装:没有彻底的封装,就是做一个基本的冗余性代码的封装
- 接口自动化的零代码封装:极限性的封装(我们后续会介绍)
2.框架封装的目的
- 简化用例的编写
- 标准化用例的格式
- 统一化框架的用法(如果你是一个人去做接口自动化测试,那你封不封装都无所谓,但如果你是一个团队的话,那就是封装的话,我们可以统一管理使用)
同时,我们封装框架的期望就是,我们只需要一个人搭建和维护框架即可,其他人专注用例的设计和执行。
接下来我们就开始进行轻量级封装了👇
二、接口自动化轻量级封装:统一请求的封装
1、统一请求封装的目的
- 统一请求封装有什么好处呢?我们的前面代码相似性极高,我们可以把相似性代码封装起来,方便复用去除重复、冗余的代码
- 我们可以设置统一的公共参数、统一的文件处理、统一的日志监控、统一的异常处理、统一的用例校验,一旦统一了,那么你维护代码的成本是很低的。
- 自动使用同一个Session,好处就是实现参数共享和cookie自动管理
2、统一请求封装的代码实现
这个封装统一请求,那我们对框架的封装,我们不是写测试用例,而是封装一个文件,我们将这个和封装的代码写在公共的目录下👇

统一请求封装的代码:
首先导入我们的requests模块
我们创建一个类,这几个类里面去封装我们的代码
将Session、公共参数变为类变量
封装一个请求的方法,参数使用不定长关键字参数,因为构造请求的时候,我们参数的个数是不确定的。
py
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、重构自动化接口测试代码
py
# 导入重构
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(待合并字典)
作用:把「待合并字典」里所有键值对,合并到「目标字典」中
两条核心判定逻辑:
- 如果目标字典 已有 和待合并字典相同的key:目标字典该key的值,会被待合并字典的值直接覆盖
- 如果目标字典 没有 待合并字典里的某个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"}
解释:
- key=application 两边都存在 → dict_a的值被dict_b覆盖(pc → app)
- key=client dict_a不存在 → 直接新增
套入当前代码逐行拆解
你的合并代码:kwargs"params".update(self.params)
拆分两个对象:
- 目标字典:kwargs"params" 用户调用传入的业务参数字典
- 待合并字典: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的

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