Pytest+requests进行接口自动化测试1.0(基础知识 + 测试用例 + request库)
接口自动化测试学习
- 一、接口自动化测试基础知识
- 1.接口自动化测试介绍
- 2.下载常用Python接口自动化相关库
- 3.接口文档
- 4.Postman(Apifox)进行简单的接口调试
- 二. 如何设计接口测试用例
- 1. 单接口测试用例(一个接口写5.6条即可)
- 2. 业务逻辑接口测试用例
- 3. 接口安全性(一般项目安全性校验较少)
- 三. requests库
- 1. 安装与导入
- 2. 基本用法 GET,POST
- 3. 高级用法
- 获取Cookie的导入语句讲解
- 4. 封装post,get请求
一、接口自动化测试基础知识
1.接口自动化测试介绍
接口自动化测试,就是通过编写代码或使用工具,自动发送请求到系统的接口(API),并自动验证返回结果是否符合预期的过程。
它属于功能自动化测试的一种,主要关注“系统之间如何通信”,而不是用户界面(UI)。
2.下载常用Python接口自动化相关库
工具/库 | 作用 |
---|---|
requests | 发送HTTP请求(GET/POST等请求) |
pytest | 核心测试框架,编写和运行测试用例 |
jsonpath-ng | 从JSON中提取数据,用于解析复杂JSON响应 |
PyYAML | 用YAML文件管理测试数据(数据与代码分离),用于读取YAML格式的测试用例或配置 |
allure-pytest | 生成可视化测试报告,配合Allure命令行可生成高级报告 |
python-dotenv | 管理不同环境的配置(如测试服、生产服,进行环境隔离),可用于 .env 文件管理环境变量 |
pytest-html | 生成基础HTML报告,快速出报告 |
requests-mock | “模拟请求”的库,用于单元测试,对进阶测试很有用 |
1)使用清华大学镜像源安装命令
清华大学 pip 镜像地址:https://pypi.tuna.tsinghua.edu.cn/simple/
2)cmd 一行安装所有库
pip install requests pytest pytest-html allure-pytest PyYAML jsonpath-ng python-dotenv requests-mock -i https://pypi.tuna.tsinghua.edu.cn/simple/
安装好后会有类似“Successfully installed …”的提示信息
3)可以逐个cmd运行以下命令,找到安装位置安装信息
pip show requests
pip show pytest
pip show jsonpath-ng
pip show PyYAML
pip show allure-pytest
pip show python-dotenv
- 举例
3.接口文档
接口文档可以找开发要,我们部门的开发是使用Apifox的,所以我直接在上面下载接口文档
此处我选择了Markdown格式下载,下载后使用Typora打开可以导出pdf格式的接口文档
接口文档内容示例:重点包含URL,接口名,接口路径,请求方法,请求参数,返回结果
4.Postman(Apifox)进行简单的接口调试
在把一个接口写到我们的测试代码前,我们要在Postman中先进行调试,调试成功再写入代码
二. 如何设计接口测试用例
1. 单接口测试用例(一个接口写5.6条即可)
- 目标:
验证单个接口在各种输入条件下的功能正确性、参数处理能力和异常处理机制
- 设计思路:
基于接口文档(如 Swagger/OpenAPI)分析请求方法、路径、参数、返回值。
覆盖正常场景、边界值、异常输入、必填项缺失等。
- 测试用例设计方法:
测试用例设计方法 | 设计思路 | 预期结果 |
---|---|---|
正向测试用例 | 1.覆盖所有必选参数 2.组合非必填参数 3.边界值测试 | 查看返回数据 |
反向测试用例 | 1.参数缺失 2. 参数为空 3.参数类型错误 4.参数超长 5.枚举值非法 | 查看返回数据 |
补充:枚举值非法 如:status: “pending2”(只允许 pending/approved)
2. 业务逻辑接口测试用例
- 目标:
验证多个接口串联后的业务流程完整性,模拟真实用户操作路径
- 常见业务场景:
用户注册 → 登录 → 获取个人信息
添加商品到购物车 → 创建订单 → 支付 → 查看订单状态
发布文章 → 审核通过 → 前端展示
- 设计思路:
梳理业务流程图(流程图或时序图)
按照“前置条件 → 操作步骤 → 预期结果”组织用例
使用自动化工具(如 Postman Collections + Tests 脚本)实现链式调用
- 测试用例设计方法:
测试用例设计方法 | 设计思路 | 预期结果 |
---|---|---|
正向测试用例 | 正常进行全流程业务 | 查看返回数据 |
反向测试用例 | 上下接口之间有参数关联,某一个环节出现问题,该接口则请求失败 | 查看返回数据 |
示例:电商下单流程
步骤 | 接口 | 说明 |
---|---|---|
1 | POST /login | 获取 token |
2 | GET /products | 查询商品列表 |
3 | POST /cart | 添加商品到购物车 |
4 | POST /order | 创建订单 |
5 | POST /pay | 支付订单 |
6 | GET /order/{id} | 查询订单状态是否为“已支付” |
设计电商下单流程测试用例:
用例编号 | 场景 | 预期结果 |
---|---|---|
TC101 | 正常下单支付全流程 | 所有接口调用成功,订单状态为“已支付” |
TC102 | 未登录下单 | 第一步无 token,应返回 401 |
TC103 | 购物车为空创建订单 | 应提示“购物车为空” |
TC104 | 重复支付 | 第二次支付应失败,防止重复扣款 |
TC105 | 库存不足 | 下单时库存 <1,应提示“库存不足” |
3. 接口安全性(一般项目安全性校验较少)
- 目标:
确保接口不会被恶意利用,防止数据泄露、越权访问、注入攻击等安全风险。
- 常见安全测试点:
安全维度 | 测试内容 | 示例 |
---|---|---|
认证与授权 | 是否校验 token?是否存在未授权访问? | 用 A 用户 token 访问 B 用户数据,应拒绝 |
越权访问 | 普通用户能否操作管理员接口? | /admin/deleteUser 普通用户调用应返回 403 |
注入攻击 | 是否防范 SQL/XSS/命令注入? | 参数传 ’ OR ‘1’='1,检查是否执行异常 SQL |
重放攻击 | 相同请求重复发送是否生效? | 同一支付请求发两次,第二次应失败 |
限流控制 | 是否防止暴力破解? | 登录接口连续失败 5 次应锁定账户或限流 |
敏感信息泄露 | 返回数据是否包含密码、身份证等? | GET /user 不应返回明文密码或银行卡号 |
数据加密 | 传输是否使用 HTTPS?敏感字段是否加密? | 检查是否强制 HTTPS,密码是否哈希存储 |
- 安全测试用例示例:
用例编号 | 安全测试场景 | 预期结果 |
---|---|---|
SEC001 | 未登录访问需认证接口 | 返回 401 Unauthorized |
SEC002 | 用户A访问用户B的数据 | 返回 403 Forbidden |
SEC003 | SQL注入测试(user_id=1’ OR ‘1’='1) | 返回 400 或正常过滤,不返回数据库错误 |
SEC004 | 敏感字段加密 | 返回的手机号、邮箱应脱敏(如 138****1234) |
SEC005 | 登录接口限流 | 连续输错5次密码,第6次提示“账户锁定” |
三. requests库
Python中一个非常流行的HTTP请求库,用于发送HTTP请求,它提供了丰富的功能,如会话、参数传递、表单提交等,同时还支持SSL证书验证。
1. 安装与导入
- 使用命令安装requests库
pip install requests
- 脚本中导入requests模块
import requests
2. 基本用法 GET,POST
- 查看get,post代码,我们可以看到需要传入的参数
- 准备参数
-- GET
url = "http://127.0.0.1:8787/coupApply/cms/goodsList"
headers = {"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"}
params = {"msgType": "getHandsetListOfCust","page": 1,"size": 20
}-- POST
url = "http://127.0.0.1:8787/dar/user/login"
headers = {"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"} # 字典类型请求头
headers = {"application/json;charset=UTF-8"}
data = {"user_name": "test01","passwd": "admin123"
}
- 发送GET请求
requests= requests.get(url="http://127.0.0.1:8787/coupApply/cms/goodsList",headers=headers,params=params)print(response.status_code) ----- 打印响应的状态码
print(response.text) ----- 打印响应的内容(文本格式,字符串)
print(response.json()) ----- (json格式,字典)
print(response.content()) ----- (二进制格式)
- 发送POST请求
response = requests.post('http://127.0.0.1:8787/dar/user/login', headers=headers,data=data, json= json)print(response.text.encode('').decode('')) ----- 输出方式和get一样,如果打印有乱码,转换格式
注意:请求头不一样,使用的参数不一样
data表单格式 --------- headers = {“Content-Type”:“application/x-www-form-urlencoded;charset=UTF-8”}
json格式 ----- headers = {“application/json;charset=UTF-8”}
- 运行结果
注意: 运行结果为{ " " }的是字符串格式, { ’ ’ }为字典格式
- GET ,POST,PUT,DELETE 的区别
import requests---- 获取用户
response = requests.get("http://api.example.com/users/1")---- 创建用户
response = requests.post("http://api.example.com/users", json={"name": "王五"})---- 更新用户(全量)
response = requests.put("http://api.example.com/users/1", json={"name": "赵六", "age": 28})---- 删除用户
response = requests.delete("http://api.example.com/users/1")
3. 高级用法
- 会话对象:使用 requests.Session() 创建会话对象,可以保持会话状态(如 Cookie)
session = requests.Session()
response = session.request(method='get',url=url,params=data,headers=headers)
常见用法: 在做接口测试时,很多系统需要先登录,然后后续操作(如查看个人信息、下单)才能成功。
使用 Session 可以自动保存 Cookie,像浏览器一样保持“登录状态”。
- 超时设置:通过 timeout 参数设置请求超时时间
response = requests.get('http://example.com', timeout=5)
常见用法:网络请求可能会因为服务器慢或网络问题一直“卡住”。设置超时可以避免程序假死。
- 自定义头部:通过 headers 参数设置请求头部
headers = {'User-Agent': 'My User Agent 1.0'}
response = requests.get('http://example.com', headers=headers)
常见用法:有些接口会校验请求头(如 User-Agent、Authorization、Content-Type),否则拒绝访问。
- 获取接口的Cookie:通过requests.utils.dict_from_cookiejar() 获取返回的 Cookie
from requests import utils
from urllib.parse import urlparsesession = requests.Session()
response = session.request(method='post',url=url1,data=data,headers=headers)
requests.utils.dict_from_cookiejar(response.cookies) --- 是 requests 官方提供的工具,能正确处理复杂的 CookieJar 对象
常见用法:登录后获取 Cookie,并用于后续请求。
获取Cookie的导入语句讲解
- requests.utils 是 Python 第三方库 requests 提供的一个工具模块,轻松处理Cookie、编码、重试等
from requests import utils
- 最常用的几个功能:
函数 | 作用 | 使用场景 |
---|---|---|
dict_from_cookiejar() | 把 CookieJar 对象转成普通字典 | 获取响应中的 Cookie 并保存 |
cookiejar_from_dict() | 把字典转成 CookieJar 对象 | 恢复 Cookie 用于后续请求 |
requote_uri() | 安全地对 URL 进行编码 | 处理中文、特殊字符的 URL |
get_encodings_from_content() | 从 HTML 内容中提取编码 | 爬虫中判断网页编码 |
- 实际例子:登录后获取 Cookie 并转为字典(常用)
import requests
from requests import utils# 发送登录请求
response = requests.post('https://example.com/login',data={'username': 'test', 'password': '123456'}
)# 获取服务器返回的 Cookie,并转为字典(方便查看或存储)
cookies_dict = utils.dict_from_cookiejar(response.cookies)
print(cookies_dict)
# 输出示例:{'sessionid': 'abc123xyz', 'user': 'test'}
- urlparse 是 Python 标准库 urllib.parse 中的一个核心函数,用于解析 URL 的各个组成部分
它可以把一个完整的 URL 拆解成协议、域名、端口、路径、参数等部分,非常适用于接口测试、路由判断、安全校验等场景。
- 实际用途举例:
1. 判断是否是 HTTPS(安全协议)if parsed.scheme == 'https':print("使用了加密传输,安全!")2. 提取 API 域名,用于统一管理base_url = f"{parsed.scheme}://{parsed.hostname}"print(base_url) # https://example.com3. 校验请求是否发往合法域名(防止误调用)allowed_hosts = ['api.example.com', 'test.example.com']if parsed.hostname not in allowed_hosts:raise ValueError("不允许请求该域名!")
- 所有高级用法
4. 封装post,get请求
- 为什么要封装?
不封装 | 封装后 |
---|---|
重复写 headers、timeout | 一次配置,全局生效 |
手动处理 token | 自动更新认证信息 |
出错难排查 | 统一日志 + 异常处理 |
难以复用 | 可被多个测试用例调用 |
无法扩展 | 易于添加新功能 |
- 示例:简单封装
--- -*- coding:utf-8 -*- 防止编码格式报错
import requests
class SendRequest:"""封装接口的请求"""def __init__(self):passdef get(self,url,params,header):"""封装get请求:param url: 请求地址:param params: 请求参数:param header: 请求头:return:"""if header is None:res = requests.get(url,params=params)else:res = requests.get(url,params=params,headers=header)return res.json()def post(self,url,data,header):"""封装post请求:param url: 请求地址:param data: 请求参数:param header: 请求头:return:"""if header is None:res = requests.post(url,data,verify=False) --- 直接传,post参数需要进行data和json的判断--- 忽略https证书校验else:res = requests.post(url,data,headers=header,verify=False)return res.json()def put(self):passdef delete(self):passdef run_main(self,url,data,header,method): --- 定义主函数"""接口请求主函数:param url:请求地址:param data:请求参数:param header:请求头:param method:请求方法:return:"""res = None --- 定义自身函数if method.upper() == 'GET': --- 把传入参数转成大写res = self.get(url,data,header)elif method.upper() == 'POST':res = self.post(url,data,header)else:print('暂时只支持“get、post请求')return resif __name__ == '__main__':url = "http://127.0.0.1:8787/coupApply/cms/goodsList"headers = {"Content-Type":"application/x-www-form-urlencoded;charset=UTF-8"}params = {"msgType": "getHandsetListOfCust","page": 1,"size": 20}method = 'GET'send = SendRequest()result = send.run_main(url=url,data=params,header=headers,method=method)print(result)