接口(API)是什么?

接口就是给前端与后端提供数据交互的通道。接口是一套完整的通信契约,包含:地址(URL)+方法(GET/POST)+请求头+请求参数+响应格式

例如,知乎点赞的接口,完整形态长这样:

① 请求部分(浏览器发给服务器)

POST https://www.zhihu.com/api/v4/answers/12345/voters

组成部分值含义
方法POST告诉服务器"我要提交数据"(不是查询,是修改)
URLhttps://.../answers/12345/voters门牌号,定位到"给 ID 为 12345 的回答投票"这个资源
请求头 HeadersAuthorization: Bearer eyJhbG...
Content-Type: application/json
"我是谁(Token)" + "我发的是 JSON 格式"
请求体 Body{"type": "up"}具体内容:"我要点的是赞同(up),不是反对(down)"

② 响应部分(服务器回给浏览器)

{
    "success": true,
    "vote_count": 1247,
    "is_voting": true
}
successtrue操作成功
vote_count1247当前总点赞数(后端算完告诉你)
is_votingtrue你现在处于"已赞同"状态

例如:前端:发起请求(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,方法,入参,出参

要素从哪看大白话
URLSwagger / 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自动读取文件,一行数据跑一次接口,不用手动改来改去

对比项CSVJSON
长什么样纯文本表格,逗号分隔带 {} [] 的结构化数据
用什么编辑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,通过代码的形式对测试进行自动化。

对比维度CookieSessionToken(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.pyPytest 靠文件名找测试
测试类必须以 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):具体要怎么做?

更多推荐