> 技术文档 > 【接口自动化】-1- 初识接口

【接口自动化】-1- 初识接口


一、什么是接口

接口涉及到四个实体:(我去饭店点餐)

我是客人 :客户端

厨师:服务器

服务员:接口

菜单:接口文档

接口定义了一套信息规则让两个系统之间互相不必知道对方的内部,只需通过接口就能进行交互

API应用编程接口:各个应用(程序之间进行高效的沟通与协作) 

 

二、HTTP接口

基于HTTP协议的接口

基本特点

  • 交互简单:请求 -> 响应模式,无状态
  • 结构简单:请求、响应,都是:行、头、体
  • 内容简单:除了体,其他均为 ASCII 内容
  • 应用广泛:至于体,支持 JSON、HTML、XML、HTML、图片、视频、音乐
  • 跨平台兼容性:电脑、电视、手机、智能家居

有冒号的是头 头完了两个换行是体 

 请求
行:方法(GET POST等)
头:描述(修饰)体
体:无限可能

响应
行:状态码
头:描述(修饰)体

缓存:CacheControl, Last-Modified

内容:Content-Type,Content-Length

体:无限可能

三、 接口测试工具实操

fiddler 的测试步骤:
1. 配置代理,使 Fiddler 录制请求
2. 点击 Replay and Edit,修改参数:构造的测试用例
3. 点击 Run、Start,发送修改后的请求,得到新的响应

优势:

  • 基于真实的请求,有连贯业务场景
  • 进行底层的精细化的操作
  • 配套的相关请求,由客户端完成,降低使用门槛

高颜值的 半自动化的接口测试工具:postman

完全自动化就是自己写python代码啦 

四、自动化测试的流程 

  1. 分析文档(理解需求)
    • 核心功能
      • 登录:
        • 方法:post
        • url: http://api.fbi.com:9225/rest-v2/login/access_token
        • body:
          • email
          • password
      • 创建任务:
        • 方法:post
        • url: http://api.fbi.com:9225/rest-v2/todo
        • body:
          • title(选填)
          • is_done(选填)
      • 查询任务列表
        • 方法:get
        • url: http://api.fbi.com:9225/rest-v2/todo
        • 参数:
          • page(选填)
          • size(选填)
      • 删除任务:
        • 方法:delete
        • url: http://api.fbi.com:9225/rest-v2/todo/{todo_id}
        • 参数:
          • todo_id
    • 次要功能
    • 辅助功能
    • 理解:
      • 有些接口有安全性要求:需要先登录再使用
      • 任务 id:需要动态获取(接口关联)

2. 设计用例

  1. 用例标题:验证登录账号可删除任务
  2. 前置条件:
    1. 已登录(得到了身份信息)
    2. 有任务(已经创建待删除的任务)
  3. 测试步骤:
    1. 得到任务 ID
    2. 调用删除接口(结果 1)
    3. 调用查询接口(结果 2)
  4. 断言(验证结果):
    1. 结果 1 成功
    2. 结果 2 失败
3. 数据准备

可登录的账号( bf@qq.com;bf123456 可测试的任务

数据驱动测试的数据⽂件

4. 编写脚本
  • 1. 纯代码脚本
  • 2. 框架的⽤例

5. 执⾏步骤

6. 断⾔结果

五、接口客户端 requests

HTTP 协议客户端事实上的标准:

  • 不支持异步
  • 不支持 HTTP2
pip install requests pytest jsonptah -i https://mirrors.aliyun.com/pypi/simple/
import requestsresp = requests.get(\"http://www.baidu.com\")print(resp.status_code)

requests.request 参数

表单

import requests# 1. 登录resp = requests.request( method=\'post\', url=\"http://api.fbi.com:9225/rest-v2/login/access_token\", data={ \"email\": \"bf@qq.com\", \"password\": \"bf123456\" })print(resp.status_code) # 人工判断if resp.status_code == 200: # 自动判断 print(\'成功\')else: print(\'失败\')

 

json

import requests# 1. 登录resp = requests.request( method=\'post\', url=\"http://api.fbi.com:9225/rest-v2/login/access_token\", json={ \"email\": \"bf@qq.com\", \"password\": \"bf123456\" })print(resp.status_code) # 人工判断if resp.status_code == 200: # 自动判断 print(\'成功\')else: print(\'失败\')

 文件

import requests# 1. 登录resp = requests.request( method=\'post\', url=\"http://api.fbi.com:9225/rest-v2/login/access_token\", files={ \"email\": open(\"详细信息显示.mp4\", \"rb\"), \"password\": open(\"惺惺惜惺惺.mp4\", \"rb\"), })print(resp.status_code) # 人工判断if resp.status_code == 200: # 自动判断 print(\'成功\')else: print(\'失败\')

requests.Response属性

状态码

响应头

resp = requests.request( method=\'get\', url=\"http://www.baidu.com\",)print(\"行:\", resp.status_code, resp.reason)print(\"头:\", resp.headers, )print(\"头:\", dict(resp.cookies), )

响应体

print(resp.content) # 二进制:传输print(resp.text) # 文本:阅读print(resp.json()) # 字典:使用 可能失败print(resp.json()[\'token_type\'])

 

requests.Session概念

cookies /session

(有多个请求构成的)同一个对话过程

共享参数
请求头(身份凭据)

共享网络
HTTP 协议基于 TCP,握手、挥手
最大端口 65535,其中 1024 保留

共享 cookies

🌰 示例场景:模拟用户登录知乎

假设我们使用requests.Session模拟用户登录知乎,并在登录后访问个人主页:

  1. 第一次请求:访问登录页面(获取初始 Cookies)
  2. 第二次请求:提交登录表单(服务器验证并设置身份 Cookies)
  3. 第三次请求:访问个人主页(自动携带登录态 Cookies)

 

🚀 代码演示与解析

import requests# 创建Session对象(核心:自动管理Cookies)session = requests.Session()# 1. 第一次请求:访问登录页面(获取初始Cookies)response1 = session.get(\'https://www.zhihu.com/signin\')print(f\"第一次请求后Session中的Cookies: {session.cookies.get_dict()}\")# 输出示例: {\'_zap\': \'xxx\', \'d_c0\': \'yyy\'}

第一次请求详细过程

  • 客户端发送请求:GET https://www.zhihu.com/signin(无 Cookies)
  • 服务器响应:
    • 返回登录页面 HTML
    • 设置初始 Cookies(如_zap=xxx; d_c0=yyy
  • Session自动捕获并存储这些 Cookies
    • 此时session.cookies包含:{\'_zap\': \'xxx\', \'d_c0\': \'yyy\'}
# 2. 第二次请求:提交登录表单(携带初始Cookies,获取身份Cookies)response2 = session.post( url=\'https://www.zhihu.com/api/v3/oauth/sign_in\', json={\'username\': \'test@example.com\', \'password\': \'123456\'})print(f\"第二次请求后Session中的Cookies: {session.cookies.get_dict()}\")# 输出示例: {\'_zap\': \'xxx\', \'d_c0\': \'yyy\', \'z_c0\': \'token123\'}

第二次请求详细过程

  • 客户端发送请求:
    • 请求头自动携带第一次获取的 Cookies:Cookie: _zap=xxx; d_c0=yyy
    • 请求体:{\"username\": \"test@example.com\", \"password\": \"123456\"}
  • 服务器响应:
    • 验证登录信息
    • 设置身份 Cookies(如z_c0=token123,表示用户已登录)
  • Session更新 Cookies:
    • 合并新 Cookies:{\'_zap\': \'xxx\', \'d_c0\': \'yyy\', \'z_c0\': \'token123\'}
# 3. 第三次请求:访问个人主页(自动携带所有Cookies)response3 = session.get(\'https://www.zhihu.com/settings/profile\')print(f\"第三次请求后Session中的Cookies: {session.cookies.get_dict()}\")# 输出示例: {\'_zap\': \'xxx\', \'d_c0\': \'yyy\', \'z_c0\': \'token123\'}

第三次请求详细过程

  • 客户端发送请求:
    • 请求头自动携带所有 Cookies:Cookie: _zap=xxx; d_c0=yyy; z_c0=token123
  • 服务器响应:
    • 验证z_c0身份令牌
    • 返回个人主页内容(只有登录用户可见)

 

📊 三次请求对比表

请求步骤 请求 URL 请求携带的 Cookies 服务器返回的 Cookies 最终 Session 中 Cookies 第一次 /signin_zap=xxx; d_c0=yyy {\'_zap\': \'xxx\', \'d_c0\': \'yyy\'} 第二次 /api/v3/oauth/sign_in _zap=xxx; d_c0=yyy z_c0=token123 {\'_zap\': \'xxx\', \'d_c0\': \'yyy\', \'z_c0\': \'token123\'} 第三次 /settings/profile _zap=xxx; d_c0=yyy; z_c0=token123 无(或更新已有 Cookies) {\'_zap\': \'xxx\', \'d_c0\': \'yyy\', \'z_c0\': \'token123\'}

 

🔑 关键结论

  1. Cookies 自动传递Session会自动在后续请求中携带之前获取的所有 Cookies,无需手动干预。
  2. Cookies 自动合并:如果服务器返回新的 Cookies(如登录后的身份令牌),Session会自动合并(覆盖同名 Cookies,保留不同名的)。
  3. 会话一致性:整个过程模拟了真实浏览器的行为,确保多个请求属于同一个用户会话。

 

🛠️ 调试技巧

如果你想查看每次请求的详细信息(包括请求头、响应头等),可以添加以下代码:

# 打印请求信息(包含Cookies)print(f\"请求URL: {response1.request.url}\")print(f\"请求头: {response1.request.headers}\")print(f\"响应状态码: {response1.status_code}\")print(f\"响应头: {response1.headers}\")

六、关联请求 

接口关联:使用另一个接口的响应内容,作为下一个接口的请求参数

  1. 大部分接口都需要:共享参数
  2. 个别接口才需要:全局变量

其他的提取方式:re、jsonpath

jsonpath 用法:

  • 针对字典
  • 使用 jsonpath 语法
  • 返回列表
import jsonpathtoken = jsonpath.jsonpath(resp.json(), \"$.access_token\")[0]s.headers.update({\"Authorization\": f\"bearer {token}\"})

 

jsonpath 讲解 🧐

jsonpath 是用于从 JSON 数据(Python 中常表现为字典、列表嵌套结构 )里提取特定内容的工具,类似字符串处理里的正则表达式,但专为 JSON 结构设计,在接口关联(从接口响应提取数据给下一个接口用)场景超实用,核心要点如下:

 

1. 基础作用 🎯

从复杂 JSON 结构里,按路径规则精准提取值。比如接口返回如下 JSON 数据(登录接口返回带 access_token ):

{ \"code\": 200, \"data\": { \"access_token\": \"abc123\", \"expires_in\": 3600 }, \"msg\": \"success\" } 

想提取 access_token,就可以用 jsonpath 按路径 $.data.access_token 快速拿到。

 

2. 语法规则(常用) 📖
语法符号 含义 示例 & 效果 $ 根节点(整个 JSON 数据的入口) $.data → 提取 data 字典内容:{\"access_token\": \"abc123\", \"expires_in\": 3600} . 取子节点、对象属性 $.data.access_token → 提取 access_token 值:abc123 [] 取列表元素、过滤对象(索引 / 条件) - 列表取值:$.data.users[0](假设 users 是列表,取第 1 个元素)
- 过滤:$.data.users[?(@.age>18)](取 users 里 age 大于 18 的对象)

 

3. 在代码里的使用步骤(结合接口关联场景) 💻

以登录后拿 token 给后续接口用为例:

import requests import jsonpath # 1. 发送登录接口请求,拿到响应 login_url = \"http://api.example.com/login\" login_data = {\"username\": \"test\", \"password\": \"123\"} resp = requests.post(login_url, json=login_data) # 2. 用 jsonpath 提取 access_token(假设响应是 JSON 格式) # resp.json() 把响应转成 Python 字典/列表,jsonpath 按规则提取 token_list = jsonpath.jsonpath(resp.json(), \"$.data.access_token\") # jsonpath 返回列表,提取第 1 个元素(实际场景需判断列表非空) token = token_list[0] if token_list else None # 3. 把 token 放到请求头,给后续接口用(比如带 Authorization) s = requests.Session() s.headers.update({\"Authorization\": f\"bearer {token}\"}) # 4. 后续接口请求自动带 token(实现接口关联) user_info_url = \"http://api.example.com/userinfo\" user_resp = s.get(user_info_url) 

 

4. 常见场景 & 优势 🌟
  • 接口关联必备:登录接口返回的 token、创建订单返回的 order_id 等,用 jsonpath 能轻松提取,传给下一个接口当参数。
  • 处理复杂结构:如果 JSON 嵌套很深(比如多层字典、列表混合),用 . 逐层找属性,比 Python 手动写循环 / 字典取值简洁太多。
  • 兼容性:不管是 requests 响应转的字典,还是普通 JSON 格式字符串转的字典,都能处理,只要结构符合 JSON 规范。

 

5. 注意事项 ⚠️
  • jsonpath.jsonpath() 返回列表,如果没找到匹配内容,返回 False 而不是空列表!所以实际用的时候,建议判断一下:
  • result = jsonpath.jsonpath(data, \"$.xxx\") if result: value = result[0] else: # 处理提取失败逻辑,比如抛异常、设默认值 raise ValueError(\"提取 xxx 失败\") 
  • 语法别和 Python 字典取值搞混,jsonpath 用 $.data.access_token ,Python 原生是 data[\"access_token\"] ,但 jsonpath 更适合复杂结构遍历。

简单说,jsonpath 就是为 JSON 提取而生的 “导航仪”,按路径找数据超方便,接口自动化里处理响应、做关联请求必学~ 结合代码多试几个嵌套结构,就能快速掌握啦 ✨

 

 七、完整的自动化实战 

import requestsimport jsonpath# 0. 创建会话,共享参数和网络链接s = requests.Session()test_user = \"bf@qq.com\"test_pass = \"bf123456\"# 1. 登录resp = s.request( method=\'post\', url=\"http://api.fbi.com:9225/rest-v2/login/access_token\", json={ \"email\": test_user, \"password\": test_pass })if resp.status_code == 200: # 自动判断 print(\'登录成功\')else: print(\'登录失败\')# 共享参数(身份凭据)token = jsonpath.jsonpath(resp.json(), \"$.access_token\")[0]s.headers.update({\"Authorization\": f\"bearer {token}\"})# 2. 查询任务列表resp = s.request( \"get\", \"http://api.fbi.com:9225/rest-v2/todo\",)if resp.status_code == 200: print(\'查询任务列表成功\') total = resp.json()[\'total\'] print(f\"当前任务总数: {total}\")# 3. 创建任务resp = s.request( \"post\", \"http://api.fbi.com:9225/rest-v2/todo\", json={\"title\": \"新的任务\", \"is_done\": False})if resp.status_code == 200: print(\'创建任务成功\') # 提取变量,以便关联 new_id = jsonpath.jsonpath(resp.json(), \"$.id\")[0] print(f\"新创建的任务ID: {new_id}\")# 4. 再次查询任务列表resp = s.request( \"get\", \"http://api.fbi.com:9225/rest-v2/todo\",)if resp.status_code == 200: print(\'再次查询任务列表成功\') new_total = resp.json()[\'total\'] print(f\"当前任务总数: {new_total}\") if new_total == total + 1: print(\'任务创建验证成功\')# 5. 删除任务resp = s.request( \"delete\", f\"http://api.fbi.com:9225/rest-v2/todo/{new_id}\",)if resp.status_code == 200: print(\'删除任务成功\')# 6. 再次查询任务列表resp = s.request( \"get\", \"http://api.fbi.com:9225/rest-v2/todo\",)if resp.status_code == 200: print(\'最后一次查询任务列表成功\') renew_total = resp.json()[\'total\'] print(f\"当前任务总数: {renew_total}\") if renew_total == new_total - 1: print(\'任务删除验证成功\') if renew_total == total: print(\'任务总数恢复验证成功\')

 

在 API 认证中,Bearer 是一种常见的身份验证方案,用于在 HTTP 请求中传递令牌(Token),表明请求者的身份。它是 OAuth 2.0 协议的一部分,也是现代 API 最常用的认证方式之一。

 

📜 Bearer Token 的基本概念

Bearer 本质是一个授权类型(Authorization Type),告诉服务器:“我持有这个令牌,请验证我的身份”。它的格式通常是:

Authorization: Bearer 
  • Bearer:固定关键字,表示使用令牌认证。
  • :服务器颁发的唯一身份令牌(如 JWT、Access Token)。

 

🔐 为什么需要 Bearer?

在传统的认证方式(如 Basic Auth)中,用户凭证(用户名 + 密码)会直接暴露在请求中,存在安全风险。而 Bearer Token 允许用户通过无状态、一次性的令牌访问受保护资源,避免了直接传递敏感信息。

常见场景

  • 用户登录后,服务器返回一个 Token(如 JWT)。
  • 客户端在后续请求中携带这个 Token,无需再次登录。
  • 服务器验证 Token 有效性,确认请求者身份。

 

🚀 代码中的 Bearer 实现

Bearer 用于传递登录后获取的 access_token

# 登录后提取Tokentoken = jsonpath.jsonpath(resp.json(), \"$.access_token\")[0]# 将Token添加到请求头,格式为 \"Bearer \"s.headers.update({\"Authorization\": f\"bearer {token}\"})
  • 注意大小写:标准写法是 Bearer(首字母大写),但有些服务器可能不区分大小写。

 

🔒 安全注意事项

  1. Token 泄露风险:Bearer Token 相当于 “数字钥匙”,一旦泄露,攻击者可冒充用户访问系统。因此:

    • 永远不要在 URL、日志或客户端代码中明文存储 Token。
    • 优先使用 HTTPS 确保传输安全。
    • 为 Token 设置合理的过期时间(如 2 小时)。
  2. 防止 CSRF 攻击:Bearer Token 通常用于 API 请求,需配合同源策略(Same-Origin Policy)或 CSRF 令牌保护。

 

🤔 Bearer vs. 其他认证方式

认证方式 优点 缺点 适用场景 Bearer Token 无状态、易于扩展、支持跨域 Token 泄露风险高 REST API、前后端分离 Basic Auth 简单、标准 明文传输密码,安全性低 内部系统、临时访问 OAuth 2.0 第三方授权、细粒度权限控制 实现复杂 开放平台(如微信登录)

 

🌟 总结

Bearer 是一种通过令牌进行身份验证的机制,在现代 API 中广泛使用。它的核心是将服务器颁发的 Token 放在请求头中,格式为 Authorization: Bearer ,从而实现安全、无状态的身份验证。

 

total = resp.json()[\'total\'] 是从 API 响应中提取数据的关键步骤!我来拆解一下它的含义和作用~ 🧐

 

1. 代码拆解:分步解释

① resp.json()
  • resp 是 requests 发送请求后得到的响应对象。
  • resp.json() 是将响应内容(通常是 JSON 格式的字符串)解析为 Python 字典或列表
    例如,如果响应是 {\"code\": 200, \"total\": 10},则 resp.json() 返回 {\'code\': 200, \'total\': 10}
② [\'total\']
  • 从解析后的字典中,通过键(Key)\'total\' 提取对应的值。
    例如,上面的例子中 resp.json()[\'total\'] 就是 10

 

2. 结合场景理解

这行代码出现在 查询任务列表 的步骤中:

# 查询任务列表resp = s.request(\"get\", \"http://api.fbi.com:9225/rest-v2/todo\")if resp.status_code == 200: total = resp.json()[\'total\'] # 提取任务总数

假设 API 返回的 JSON 是这样的:

{ \"code\": 200, \"message\": \"success\", \"data\": { \"items\": [ {\"id\": 1, \"title\": \"任务1\"}, {\"id\": 2, \"title\": \"任务2\"} ], \"total\": 2 # 这是任务总数 }}

 

那么 resp.json()[\'total\'] 就会提取出 2,表示当前有 2 个任务。

 

3. 为什么需要这个值?

在自动化测试中,这个值通常用于 验证业务逻辑

  • 比如创建新任务后,检查总数是否增加 1。
  • 删除任务后,检查总数是否减少 1。
# 创建任务前的总数total = resp.json()[\'total\'] # 创建任务...# 创建后的总数new_total = resp.json()[\'total\'] # 验证总数是否增加1if new_total == total + 1: print(\'任务创建验证成功\')

4. 潜在风险与优化

① Key 不存在的情况

如果 API 返回的 JSON 中没有 total 键,直接访问会报错。
优化建议:使用 .get() 方法,不存在时返回默认值(如 None)。

total = resp.json().get(\'total\') # 不存在时返回Noneif total is not None: # 处理逻辑else: print(\"响应中缺少\'total\'字段\")
② 响应格式变化

如果 API 升级后返回格式变了(如 total 移到 data.total),代码会失效。
优化建议:使用 jsonpath 更灵活地提取数据(已经在用啦!)。

total = jsonpath.jsonpath(resp.json(), \'$.total\')[0] # 更健壮的写法

🌟 总结

这行代码的核心作用是:从 API 响应中提取 任务总数,用于后续的业务逻辑验证(如创建 / 删除任务后检查数量变化)。它是接口自动化测试中 数据断言 的关键步骤!