接口自动化测试
接口(API)是什么?
接口就是给前端与后端提供数据交互的通道。接口是一套完整的通信契约,包含:地址(URL)+方法(GET/POST)+请求头+请求参数+响应格式
例如,知乎点赞的接口,完整形态长这样:
① 请求部分(浏览器发给服务器)
POST https://www.zhihu.com/api/v4/answers/12345/voters
| 组成部分 | 值 | 含义 |
|---|---|---|
| 方法 | POST | 告诉服务器"我要提交数据"(不是查询,是修改) |
| URL | https://.../answers/12345/voters | 门牌号,定位到"给 ID 为 12345 的回答投票"这个资源 |
| 请求头 Headers | Authorization: Bearer eyJhbG...Content-Type: application/json | "我是谁(Token)" + "我发的是 JSON 格式" |
| 请求体 Body | {"type": "up"} | 具体内容:"我要点的是赞同(up),不是反对(down)" |
② 响应部分(服务器回给浏览器)
{
"success": true,
"vote_count": 1247,
"is_voting": true
}
success | true | 操作成功 |
vote_count | 1247 | 当前总点赞数(后端算完告诉你) |
is_voting | true | 你现在处于"已赞同"状态 |
例如:前端:发起请求(request),API:接收请求,返回相应(response),后端:处理业务逻辑

f12开发者工具模式
1.网络⭐⭐⭐ (Network)
作用:抓包,看接口请求和相应,定位前后端Bug
| 截图里的东西 | 测试开发用它干什么 |
|---|---|
| Fetch/XHR 筛选器 | 只看 API 接口请求,过滤掉图片/CSS/JS 噪音 |
| 状态码(200/101/404/500) | 一眼判断接口是成功还是失败(200 成功,101 协议升级,404 未找到,500 服务端错误) |
| Payload(点请求后右侧) | 看前端传给后端什么参数,验证参数对不对 |
| Response(点请求后右侧) | 看后端返回什么数据,验证响应结构/字段值 |
| 时间列 | 看接口慢不慢,性能瓶颈定位 |
| 保留日志 | 页面刷新后请求还在,方便追踪跳转前后的接口 |
名称
1.touch ——状态201,fetch
类型是fetch:说明这是前后端交互的API接口,不是图片不是脚本
2.apm ——状态200,fetch
应用性能监控,网站收集自己的网页的性能(卡不卡,加载快不快),发回自家分析系统
3.batch ——fetch
批量,前端把好几件小事打包成一趟快递一起发送,提高效率
fetch请求
点击任意一个fetch请求(获取数据)

1.Headers 标头
请求地址URL,请求方式(get/post),Cookie,Token身份令牌
常见的状态码
【2XX成功类】
1.200 OK
请求成功,服务器已返回请求的资源,常用于查询,更新
2.201 Created
请求成功,并且服务器创建了新的资源。用于新建资源成功时。(提交注册表单,服务器成功创建新用户账号)
【3XX重定向类】
1.301 永久重定向
请求的资源已被永久移动到新的URL,后续访问应直接使用新地址。例如网站换域名,旧域名会自动跳转到新域名。浏览器会缓存这个跳转,下次直接访问新地址
2.302 临时重定向
【4XX客户端错误类】 ----------------前端/测试重点排查区
1.400 Bad Request
请求报文存在语法错误或参数格式不符合接口约定,服务器无法解析。类如:
-
接口要求传 JSON,你却传了纯文本
-
必填字段没传
-
手机号字段传了字母
定责:客户端Bug(前端传参不对),或测试用例数据构造错误
2.401
请求需要用户身份验证,但是客户端未提供或提供了无效的认证信息。例如:
-
没登录就调用"获取个人订单"接口
-
Token 过期或伪造
3.403
服务器理解请求,但是拒绝执行,通常因为客户端权限不够
3.404
服务器找不到请求对应的资源(URL 路径错误或资源已被删除)
【5XX服务端错误类】 ------------------后端问题
1.500
服务器内部遇到意外情况,无法完成请求。这是最通用的服务端错误码,例如:
-
前端传了一个合法但极端的参数(如超长的特殊字符),后端没做校验直接抛异常
-
数据库连接断开,后端代码直接奔溃
2.502
网关或代理服务器从上游服务器收到了无效相应
看网关日志,确认上游服务是否健康
3.503
服务器当前无法处理请求,通常是临时过载或正在维护.通常是临时性的,刷新可能就好了
2.Payload 负载
前端传给后端的参数。比如 {"user_id": "12345"}
3.Response 相应
后端返回的hson数据,比如用户昵称,头像
什么是接口测试?
定义:接口测试是绕过前端页面,直接模拟客户端向服务端发送的请求,验证服务器返回的相应数据是否符合预期的测试方法
接口测试是针对软件系统组件之间通信接口的测试活动。它绕过用户界面(UI),直接对应用程序编程接口(API)发送HTTP请求,验证接口的功能性,健壮性,安全性及数据交换正确性。与UI测试不同,接口测试关注的是“数据在前后端之间的流通性,每一环是否正确”
| 测试类型 | 你实际看到的操作 | 本质上在测什么 |
|---|---|---|
| UI 测试 | 你在知乎APP里点"赞同"按钮,看按钮变蓝、数字从 1246 变成 1247、有没有弹出"已赞同"的提示 |
测的是前端展示层——按钮样式、 动画效果、页面跳转 |
| 接口测试 | 你不打开知乎APP,直接用一段代码向知乎服务器发请求:POST /api/vote?answer_id=12345&type=up,然后看服务器回什么 | 测的是后端业务层——服务器有没有真的把点赞数+1、有没有检查你有没有登录、有没有重复点赞限制 |
-
UI 测试 = 你站在用户视角,用眼睛看、用手点,验证"我看到的对不对"
-
接口测试 = 你站在系统内部视角,直接敲服务器的门,验证"你内部处理得对不对"
区分三种测试:
UI自动化测试 -> 模拟人工点击,输文字(测页面)
接口测试 -> 模拟系统之间传数据(测后台逻辑)
单元测试 -> 测代码里的一个函数(测最小单元)
为什么要做接口测试?
/\
/ \ UI 自动化测试(少而精,成本高)
/----\ ← 运行慢、维护难、容易挂
/ \
/--------\ 接口测试(重点投入,性价比最高)
/ \ ← 运行快、覆盖广、稳定可靠
/------------\
/ \
/________________\ 单元测试(开发写,测试辅助)
原理总结:Bug越早发现越好,修复成本越低。接口测试卡在中间层,既能验证逻辑,又能比UI测试快10倍以上
| 原因 | 大白话解释 | 真实场景 |
|---|---|---|
| 1. 测得更早 | 前端页面还没画完,后端接口先做好了,可以先测 | 前端还在改按钮颜色,你已经把登录逻辑Bug找出来了 |
| 2. 测得更深 | 前端会拦住的输入,直接绕过去发给服务器 | 前端限制了密码长度6-20位,你直接发1位密码,看服务器有没有校验 |
| 3. 测得更稳 | 不受页面改版影响 | UI一变,自动化脚本全挂;接口逻辑没变,脚本不用改 |
| 4. 测得更快 | 一个接口请求毫秒级完成 | 跑1000个接口用例 < 5分钟;跑1000个UI用例 > 2小时 |
| 5. 方便自动化 | 发请求、收响应,全是文本,好断言 | 判断"返回code=200"比判断"页面弹出了绿色对勾"简单100倍 |
接口测试的实现方式
手动工具层->代码自动化层->工程化集成层
手工工具层(Postman/apifox/JMeter)
代码自动化(python+requests+pytest)
工程化集成(Jenkins/github CI+Mock+Allure)
HTTP协议
超文本传输协议,规范了浏览器和服务器之间数据传输的格式
它规定了请求报文格式,相应报文格式,请求方法有几种,状态码有几种
┌─────────────────────────────────────────────┐
│ 第4层 应用层 │ HTTP / HTTPS / FTP / DNS │
│ │ ← 你写的代码直接操作这一层 │
├─────────────────────────────────────────────┤
│ 第3层 传输层 │ TCP(可靠传输)/ UDP(快但丢包)│
│ │ ← 负责"数据包能不能完整送到" │
├─────────────────────────────────────────────┤
│ 第2层 网络层 │ IP(找路)/ ICMP(ping) │
│ │ ← 负责"从哪台电脑发到哪台电脑" │
├─────────────────────────────────────────────┤
│ 第1层 链路层 │ 以太网 / WiFi / 网线 / 光纤 │
│ │ ← 负责"电信号/光信号怎么传" │
└─────────────────────────────────────────────┘
http的特性:
客户端/服务器模式
简单快速
无状态
http不具备记忆,每一次请求都是一个全新的请求,比如你打开学校的教务系统,输入账号密码登录之后,服务器会返回给你一个cookie/Token,相当于表名身份的,因为下一次你的请求,比如跳转到选课页面,服务器已经不记得你是谁了,如果没有身份令牌,则还需要再一次输入账号密码
无连接
服务器和浏览器只在发一次请求,回一次响应的这一瞬间连接,传输完之后立马断开
HTTPS和HTTP的区别
HTTP是明文传输协议,数据不加密,端口是80。HTTPS在HTTP和TCP之间增加了TSL/SSL加密层。通过数字证书验证服务器身份,对传输数据加密,端口443.HTTPS能防止中间人窃听,篡改,冒充。
整体格式:
大致就是:
请求行
请求头
(空一行)
请求体
POST /api/login HTTP/1.1 ← 请求行(方法 + URL + 协议版本)
Host: library.dgut.edu.cn ← 请求头开始
Content-Type: application/json ← 告诉服务器:我发的是JSON格式
Content-Length: 45 ← Body有多少字节
Authorization: Bearer xxx ← 身份凭证(Token)
User-Agent: Mozilla/5.0 ← 我是啥浏览器
Accept: application/json ← 我希望你回JSON给我
Connection: keep-alive ← TCP连接别断,我还要发别的请求
← 空行(固定格式,区分头和体)
{"student_id":"2024414300116"} ← 请求体(Body,POST/PUT才有)
需要记住的是文本类型Content-Type:
text/html:HTML格式
text/plain:纯文本格式
image/jpeg:jpg图片格式
application/json:JSON数据格式
application/x-www-urlencoded:表单默认的提交数据格式
multipart/form-data:在表单中进行文件上传时使用
请求方法:
get:查询
post:添加(登录,注册)
put:修改
delete:删除
接口测试的流程
步骤1:读接口文档(或者自己抓包看)
搞清楚四件事:
URL,方法,入参,出参
| 要素 | 从哪看 | 大白话 |
|---|---|---|
| URL | Swagger / YApi / 抓包 | 接口地址在哪 |
| 方法 | GET / POST / PUT | 怎么跟服务器打招呼 |
| 入参 | Query / Body / Header | 要传什么数据过去 |
| 出参 | 响应 JSON / HTML | 服务器会回什么 |
步骤2:设计测试用例
正向场景
异常场景:空值,非法值,越界值
安全场景:越权访问,未登录
步骤3:执行测试
方式A:postman手动执行
方式B:自动化执行
步骤4:发现bug,提单
步骤5:输出测试报告
接口测试的维度
兼容,功能,性能,安全,契约,可靠
┌─────────────────────────────────────────────────────────────┐
│ ① 功能维度 → 数据对不对?逻辑走不走得通? │
│ ② 性能维度 → 快不快?并发扛不扛得住? │
│ ③ 安全维度 → 没权限的人能不能偷数据?参数有没有漏洞? │
│ ④ 兼容维度 → 不同客户端/版本调接口,返回一不一样? │
│ ⑤ 可靠维度 → 网络断了/服务器挂了,能不能恢复? │
│ ⑥ 契约维度 → 接口文档和实际代码是不是一回事? │
└─────────────────────────────────────────────────────────────┘
功能测试
主要测试接口在正常输入和异常输入下,返回的数据和业务逻辑是否正确。
要覆盖三类场景:正向流程验证功能正确,异常流程验证参数缺失,类型错误,格式非法输入时的容错,还有边界值验证极限长度和数值
性能测试
接口在高并发、大数据量、长时间运行下的表现
| 子维度 | 测试点 | 具体案例 |
|---|---|---|
| 响应时间 | 单用户调用,多久返回? | 正常登录应该在 200ms 以内,超过 1 秒用户体验就差 |
| 并发压力 | 100 个人同时登录,服务器挂不挂? | 用 JMeter/Locust 模拟 100 并发,看错误率是不是 0% |
| 吞吐量 | 每秒能处理多少请求?(QPS/TPS) | 系统宣传支持 1000 QPS,实际压测看能不能达到 |
| 稳定性 | 连续跑 8 小时,内存会不会泄露? | 登录接口连续压测,看服务器内存曲线是不是一直涨 |
| 瓶颈定位 | 慢在哪?SQL?网络?代码? | 登录慢可能是因为 SELECT 没加索引,查数据库花了 500ms |
安全测试
接口在未授权、恶意输入、越权访问时,能不能守住数据
| 子维度 | 测试点 | 具体案例 |
|---|---|---|
| 鉴权绕过 | 删掉 Token,还能不能调接口? | 不带 Authorization 调 /api/sys/user → 必须返回 401 |
| 越权访问 | 普通员工能不能调管理员接口? | 用普通用户 Token 调删除部门接口 → 应该返回 10003 权限不足 |
| SQL 注入 | 参数里塞 SQL 语句,后端有没有过滤? | username 传 ' OR 1=1 -- → 如果后端拼接 SQL,可能泄露全库数据 |
| 敏感信息泄露 | 响应里有没有返回密码、Token? | 查询员工列表接口,返回的 JSON 里包含 password 字段 → 严重安全 Bug |
| 暴力破解 | 无限次试密码,后端有没有限制? | 连续输错 5 次密码,账号应该被锁定或要求验证码 |
| XSS 注入 | 参数里塞脚本标签,后端有没有转义? | username 传 <script>alert(1)</script>,如果存入数据库且前端直接渲染,就会弹窗 |
兼容测试
不同客户端、版本、数据格式下调接口,结果是否一致
postman参数化
什么是参数化?
参数化就是把测试数据从“代码/body中拿出来”,放到外部文件(CSV/JSON)里。postman自动读取文件,一行数据跑一次接口,不用手动改来改去
| 对比项 | CSV | JSON |
|---|---|---|
| 长什么样 | 纯文本表格,逗号分隔 | 带 {} [] 的结构化数据 |
| 用什么编辑 | Excel、WPS、记事本 | VS Code、记事本(Excel 打不开) |
| 适合什么数据 | 简单的二维表格(手机号、密码、预期结果) | 嵌套复杂对象(如整个 JSON Body) |
| Postman 支持 | ✅ 完美支持 | ✅ 原生支持 |
| 人类可读性 | 极高,像 Excel | 中等,有括号嵌套 |
| 体积 | 极小 | 稍大 |
| 嵌套能力 | ❌ 不支持(只能平铺) | ✅ 支持对象嵌套数组 |
-
数据简单(手机号、密码、预期状态码)→ CSV(用 Excel 写,舒服)
-
第一行是变量名(英文,不能有空格,逗号分隔)
-
第二行开始是数据(一行 = 一次请求)
-
逗号是英文逗号
,,不是中文逗号, -
文本里有逗号怎么办? 用双引号包起来:
"张三,开发部" -
保存为 UTF-8 编码,否则中文乱码
-
mobile,password,expected_code,description 13800000002,929itheima.CN032@20260501,10000,正确登录成功 13800000002,wrongpassword,20001,密码错误 ,929itheima.CN032@20260501,99999,手机号为空 1380013800,929itheima.CN032@20260501,20001,手机号10位 13800000003,929itheima.CN032@20260501,20001,手机号未注册 -
数据复杂(整个请求 Body 是嵌套 JSON)→ JSON
-
最外层是
[]数组,里面每个{}是一组数据 -
键必须是双引号字符串
-
字符串值必须是双引号
-
最后一个字段后面不要加逗号
-
保存为
.json文件,UTF-8 编码 -
[ { "mobile": "13800000002", "password": "929itheima.CN032@20260501", "expected_code": 10000, "description": "正确登录成功" }, { "mobile": "13800000002", "password": "wrongpassword", "expected_code": 20001, "description": "密码错误" }, { "mobile": "", "password": "929itheima.CN032@20260501", "expected_code": 99999, "description": "手机号为空" } ]
读取文件数据
根据使用位置不同,有两种方法
1.请求参数(URL,请求头,请求体)使用 数据文件 中的数据
使用{{}}包裹CSV文件第一行的字段名或JSON文件的KEY值
2.代码(Tests)中,使用 数据文件 中的数据
使用postman内置的关键字,data.csv文件的字段名 或 data.json文件的key名
postman生成测试报告
一、环境准备
1.电脑安装了node.js运行环境
2.安装newman(postman命令行工具)
npm install -g nowman(-g选项:设置成系统环境变量)
3.安装HTML报告插件
npm install -g newman-reporter-htmlextra
二、步骤
需要导出用例集(postman_collection.json),环境变量.json,关联文件(csv,json)
然后打开终端输入命令:
newman run 接口测试2.postman_collection.json -e IHRM环境.postman_environment.json -d 关联文件.csv -r htmlextra --reporter-htmlextra-export ./report.html
requests接口自动化测试
什么是request?
requests就是一个python版的postman,通过代码的形式对测试进行自动化。
| 对比维度 | Cookie | Session | Token(JWT) |
|---|---|---|---|
| 存在哪里 | 浏览器/客户端 | 服务器(内存/Redis) | 客户端(LocalStorage/Cookie) |
| 存什么 | SessionID 或少量数据 | 完整的用户状态 | 加密的用户信息 |
| 谁生成 | 服务器 Set-Cookie | 服务器创建 | 服务器签发 |
| 服务器状态 | 有状态(要配合 Session) | 有状态 | 无状态 |
| 安全性 | 低(可被查看篡改) | 中(数据在服务端) | 高(签名防篡改) |
| 大小限制 | 4KB | 无限制 | 无限制(但太大会影响传输) |
| 分布式支持 | 差 | 差(需 Redis 共享) | 好(任何服务器都能验) |
| 跨域支持 | 受 SameSite 限制 | 不涉及 | 天然支持(Header 传输) |
| 典型项目 | 传统 JSP/PHP 网站 | 传统 Web 项目 | 前后端分离/Vue/React/小程序 |
什么是Cookie?
Cookie就是服务器让浏览器存储的小型文本,由服务器通过Set-Cookie下发,通常大小为4KB,以后每次请求同域名时,浏览器自动带过去
-
存在浏览器里(客户端)
-
大小限制约 4KB
-
每次请求自动携带,不用你手动写代码
-
可以被用户查看、篡改(除非设 HttpOnly/Secure)
什么是session?
session就是服务器自己存的“用户状态表”,每个登录用户占一行,通过Cookie里面的SessionID找到这行记录
-
存在服务器里(内存/Redis/数据库)
-
可以存大量数据(用户权限、购物车、浏览历史)
-
通过 Cookie 里的
sessionId关联 -
服务器重启可能丢失(如果存在内存里)
┌─────────────────────────────────────────────────────────┐
│ 小网站(单机) │
│ Session 存在服务器内存(RAM)里 │
│ → 服务器重启,所有 Session 全丢,所有人被踢下线 │
├─────────────────────────────────────────────────────────┤
│ 中型网站(多台服务器 + Redis) │
│ Session 存在 Redis 里(专门的缓存数据库) │
│ → 服务器 A 登录,请求打到服务器 B,B 也能去 Redis 查到 Session │
│ → 服务器重启不影响,因为数据在 Redis 里 │
├─────────────────────────────────────────────────────────┤
│ 大型网站/现代架构(Token/JWT) │
│ 服务器不存 Session!Token 自带用户信息 │
│ → 服务器只验 Token 签名,不用查内存/Redis │
└─────────────────────────────────────────────────────────┘
分布式集群:用户请求轮流分给不同 服务器处理
Redis:是一个临时公共仓库,专门用来存Token,验证码,临时数据。所有服务器共享一份session数据,不管落到哪台服务器,都能查到登录状态,实现登录状态共享、不掉线。
什么是Token?
服务器不存任何状态,把用户信息加密后生成一串字符串发送给你,你每次请求带过来,服务器验签名就知道你是谁
-
存在客户端(LocalStorage/内存/Cookie 里都可以)
-
服务器不存状态(无状态)
-
自带用户信息(用户ID、角色、过期时间)
-
通过数字签名防篡改
-
天然适合分布式/微服务(任何一台服务器都能验)
Requests中怎么实现Cookie/Session?
方式一:Requests中自动处理Cookies
import requests
# 创建一个 Session 对象(不是 requests.post,是 requests.Session().post)
# Session 就像一个"保持登录状态的浏览器窗口"
session = requests.Session()
# ① 登录(服务器返回 Set-Cookie,Session 对象自动存)
login_resp = session.post(
url="http://localhost:9527/admin/auth/login",
json={"username": "admin123", "password": "admin123", "code": ""}
)
# 注意:这里不用手动提取 Token/Cookie!Session 对象自动处理了!
# ② 查广告列表(Session 自动带上 Cookie,就像浏览器自动带一样)
list_resp = session.get(
url="http://localhost:9527/admin/ad/list",
params={"page": 1, "limit": 20}
)
print(list_resp.json())
pytest复习
关于pytest的相关知识,在我之前的文章【selenium自动化测试】已经讲过了,这里我们简单回顾一下。那么到底什么是pytest?
pytest就是一个测试框架,用来组织和运行测试
而request则是HTTP客户端库,用来发送网络请求
两者结合共同用于做接口测试
fixture就是测试夹具,就是用装饰器@pytets.fixture装饰一个普通的函数,这个函数就成了测试夹具。它是一个测试准备工具,专门处理测试前/后的公共逻辑。有两个参数(scope和autouse),一个是执行的范围,一个是自动调用。但是如果涉及到传参的化还得手动传参。
| 层级 | 命名规则 | 例子 | 说明 |
|---|---|---|---|
| 测试文件 | 必须以 test_ 开头或 _test 结尾 | test_litemall_login.py | Pytest 靠文件名找测试 |
| 测试类 | 必须以 Test 开头(大写 T) | class TestAd: | 类里可以包多个测试方法 |
| 测试函数 | 必须以 test_ 开头 | def test_login(): | 一个函数 = 一条测试用例 |
| 测试方法 | 类里的方法同样 test_ 开头 | def test_delete_ad(): | 类里的测试函数 |
运行方式:命令行
# ① 运行当前目录所有测试文件
pytest
# ② 运行指定文件
pytest test_litemall.py
# ③ 显示详细过程(-v = verbose)
pytest -v
# ④ 显示 print 输出(-s = 捕获输出)
pytest -v -s
# ⑤ 只运行包含 "login" 的用例
pytest -v -k login
# ⑥ 生成 HTML 报告
pytest --html=report.html
可能说到这里,大家还是有点不明白,这里问大家一个问题,你设计一个接口测试,用requesrt库发送HTTP请求,那么你登录拿到Token,但是你在同一个网址发送其他请求,还是需要Token,那么你还要再写一遍吗?还有一个最重要的是,它可以测试前的准备+测试完之后的结尾工作。
fixture的2种使用风格
1.函数参数注入(最常用)
import pytest
import requests
# ① 定义 Fixture:scope="session" 表示整个测试会话只执行一次
@pytest.fixture(scope="session")
def admin_token():
"""登录 litemall,返回 Token,所有用例共享"""
resp = requests.post(
url="http://localhost:9527/admin/auth/login",
json={"username": "admin123", "password": "admin123", "code": ""}
)
token = resp.json()['data']['token']
print(f"\n【Fixture】登录成功,Token: {token[:20]}...")
return token
# ② 使用:把 fixture 名作为参数传进去,Pytest 自动注入
def test_query_ad(admin_token):
"""查询广告列表"""
resp = requests.get(
url="http://localhost:9527/admin/ad/list",
params={"page": 1, "limit": 20},
headers={"X-Litemall-Admin-Token": admin_token}
)
assert resp.json()['errno'] == 0
# ③ 另一个测试函数,同样注入,但不会再登录(因为 scope="session")
def test_delete_ad(admin_token):
"""删除广告"""
# admin_token 直接可用,不用重新登录
...
可见,@pytest.fixture用于函数
2.类装饰器@pytest.mark.usefixtures
import pytest
# 定义 Fixture(同上)
@pytest.fixture(scope="session")
def admin_token():
...
# 整个类里的所有测试方法,自动注入 admin_token
@pytest.mark.usefixtures("admin_token")
class TestAd:
def test_query_ad(self, admin_token):
# 直接用 admin_token
...
def test_delete_ad(self, admin_token):
# 直接用 admin_token
...
可见,@pytest.mark.usefixtures用于类级别的,自动注入,至于为什么都自动注入了,类里的函数的参数仍然需要传参,这是因为你这个测试夹具函数有返回值,需要手动传参,有变量接收
PyMySQL数据库操作
为什么接口测试要连数据库?
如果接口测试只测试返回值的话,最多只能验证“服务器没蹦,格式对了”,但是验证不了“数据真的写进去了,真的删掉了,真的改对了”
为什么接口测试不靠谱?(4种常见的Bug)
Bug1:接口说成功,数据库没写(假成功)
你调 litemall 删除广告接口:
─────────────────────────────────────────
请求:POST /admin/ad/delete
响应:{"errno":0, "errmsg":"成功"}
你以为:广告删了,测试通过
查数据库:
─────────────────────────────────────────
SELECT deleted FROM litemall_ad WHERE id=2;
结果:deleted = 0 ← 根本没改!
真相:
后端代码忘了加 conn.commit()(事务没提交)
或者只改了 Redis 缓存,没写 MySQL
或者代码抛异常被 try-except 吞掉,返回了假成功
这叫:业务逻辑与持久化层不一致
那么,什么是持久化层?
持久化层就是系统中负责将数据永久保存到存储介质(MySQL,Redis,文件系统)的层级,特征是断电不丢失。接口测试只验证返回值,无法确认数据是否真的写入持久化层。后端可能存在事务未提交,写错表,缓存不一致等问题,所以必须通过PyMysql直接查询数据库,验证持久化层的真实状态。
Bug2:接口 说成功,但是写错表/写错字段了
你调添加员工接口:
─────────────────────────────────────────
请求:添加员工"张三"
响应:{"errno":0, "errmsg":"成功"}
查数据库:
─────────────────────────────────────────
SELECT * FROM litemall_user WHERE username='张三';
结果:查不到!
再查另一张表:
SELECT * FROM litemall_ad WHERE name='张三';
结果:卧槽,员工被写到广告表里了!
真相:
后端代码 copy-paste,把 INSERT INTO litemall_user 写成了 INSERT INTO litemall_ad
接口照样返回成功,因为确实 INSERT 成功了,只是插错表了
这叫:数据落点错误
Bug3:接口说成功,但数据格式/值不对
你调修改密码接口:
─────────────────────────────────────────
请求:把密码改成 "NewPass123"
响应:{"errno":0, "errmsg":"成功"}
查数据库:
─────────────────────────────────────────
SELECT password FROM litemall_admin WHERE id=1;
结果:password = "NewPass123" ← 明文存储!
真相:
接口确实写进去了,但密码应该是 MD5/BCrypt 加密后的字符串
后端忘了加密,直接把明文存进数据库
接口返回成功,但安全规范被违反了
这叫:业务规则在持久化层未生效
Bug 4:接口说成功,但关联数据没清理(脏数据)
你调删除订单接口:
─────────────────────────────────────────
请求:删除订单 ID=100
响应:{"errno":0, "errmsg":"成功"}
查数据库:
─────────────────────────────────────────
SELECT * FROM litemall_order WHERE id=100;
结果:deleted=1 ← 主表删了
SELECT * FROM litemall_order_goods WHERE order_id=100;
结果:还有 3 条记录!← 子表没删
真相:
后端只删了订单主表,忘了删订单商品关联表
接口返回成功,但数据库留下孤儿数据(脏数据)
这叫"级联删除缺失,导致数据孤儿"
操作步骤
1.导包import pymysql
2.创建 连接 conn=pymysql.connect()
3.创建 游标(数据库查询结果的指针) sursor=conn.cursor()
4.执行sql语句:cursor.execute("sql语句")
查询语句select ————不修改数据库,会返回结果集
从结果集中提取想要的数据cursor.fetch*
【cursor.fetchone():取第一条,指针下移】
【cursor.fetchall():全部取出来,塞成列表】
【cursor.fetchmany(size):取N条】
增删改语句insert delete update ————没有结果返回,会修改数据库
执行成功:提交事务 conn.comment()
执行失败:回滚事务conn.rollback()
5.关闭游标cursor.close()
6.关闭连接 conn.close()
代码演示
cursor.execute("SELECT id, name FROM litemall_ad")
# 方式 1:一条一条取(适合不确定有多少条)
while True:
row = cursor.fetchone()
if row is None: # 取完了,返回 None
break
print(f"广告 ID={row[0]}, 名称={row[1]}")
# 方式 2:全取出来(适合小表)
cursor.execute("SELECT id, name FROM litemall_ad")
all_rows = cursor.fetchall()
print(all_rows)
# 输出:[(2, '活动 美食节'), (3, '活动 母亲节'), ...]
# 方式 3:分批取(适合大表)
cursor.execute("SELECT * FROM litemall_order") # 100万条订单
while True:
batch = cursor.fetchmany(1000) # 每次取 1000 条
if not batch:
break
print(f"处理了 {len(batch)} 条")
普通游标 VSDictCursor
普通游标(默认):返回元组(有序,不可改变)
cursor = conn.cursor() # 默认
cursor.execute("SELECT id, name, deleted FROM litemall_ad WHERE id=2")
result = cursor.fetchone()
print(result) # 输出:(2, '活动 美食节', 0)
print(result[0]) # 2 ← id
print(result[1]) # '活动 美食节' ← name
print(result[2]) # 0 ← deleted
DictCursor:返回字典
cursor = conn.cursor(pymysql.cursors.DictCursor) # ← 加这个
cursor.execute("SELECT id, name, deleted FROM litemall_ad WHERE id=2")
result = cursor.fetchone()
print(result)
# 输出:{'id': 2, 'name': '活动 美食节', 'deleted': 0}
print(result['deleted']) # 0 ← 一眼就知道是 deleted 字段!
print(result['name']) # '活动 美食节'
step1:原始写法
import pymysql
# ① 连接数据库(5个参数,从 application.yml 抄的)
conn = pymysql.connect(
host='localhost',
port=3307, # ← 你的映射端口!
user='litemall',
password='litemall123456',
database='litemall',
charset='utf8'
)
# ② 创建游标(DictCursor 让结果变成字典)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# ③ 执行 SQL:查刚才创建的管理员
sql = "SELECT id, username, password, add_time FROM litemall_admin WHERE username = %s"
cursor.execute(sql, ('adic12',)) # ← %s 是占位符,防注入
# ④ 取结果
result = cursor.fetchall()
print(f"查询结果:{result}")
# ⑤ 关闭(顺序不能反:先游标,后连接)
cursor.close()
conn.close()
step2:函数封装(减少代码冗余)
import pymysql
# ==================== 配置区(只写一次,全局用) ====================
DB_CONFIG = {
"host": "localhost",
"port": 3307,
"user": "litemall",
"password": "litemall123456",
"database": "litemall",
"charset": "utf8"
}
# ==================== 函数封装(实习够用) ====================
def db_query(sql, params=None):
"""
查询数据库:传入 SQL + 参数,自动连接→执行→返回→关闭
"""
# 1. 连接
conn = pymysql.connect(**DB_CONFIG)
# 2. 游标
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 3. 执行
cursor.execute(sql, params)
# 4. 取结果
result = cursor.fetchall()
# 5. 关闭
cursor.close()
conn.close()
# 6. 返回
return result
# 查管理员
result = db_query(
"SELECT id, username, password FROM litemall_admin WHERE username = %s",
('adic12',)
)
print(result)
# 输出:[{'id': 6, 'username': 'adic12', 'password': '$2a$10$...'}]
持续集成(CI)
代码提交后,服务器自动帮你跑测试,出报告,发通知,不用每天手动点点点
相关工具:
| 工具 | 作用 | 实习面试掌握程度 |
|---|---|---|
| Git | 代码版本管理 | 必须会 git add/commit/push |
| GitHub/GitLab | 代码仓库 + CI 配置 | 了解 .gitlab-ci.yml 或 GitHub Actions |
| Jenkins | 最主流的 CI 调度工具 | 了解概念,能说出"定时构建、流水线、插件" |
| 钉钉/邮件 Webhook | 测试结果通知 | 了解原理 |
那么jenkins就是我们用来持续集成的工具,给他安排好流程之后,当触发某个条件时,会自动帮你完成【拉代码——装环境——跑测试——出报告——发通知】的全部流程。
这里说一下jenkins中几个概念
构建(Build):执行一次完整的流程
触发器(Build Trigger):什么时候执行流程
-Build periodically(定时构建):
格式:分 时 日 月 周
*代表所有时间,/n代表每隔n单位执行,a-b区间范围,H随机偏移(避免大量任务同一秒扎堆运行)
-Poll SCM(轮询源码仓库):每隔一段时间取gitee检查一下,代码有变化就运行,没变化就不运行
构建步骤(Build Step):具体要怎么做?
更多推荐




所有评论(0)