一、Requests 库核心函数 (HTTP 请求)
用于发送各种类型的 HTTP 请求,是接口测试的基础。
1. 顶层请求函数
| 函数名 | 用途 | 常用参数说明 |
|---|---|---|
requests.get() |
发送 GET 请求 | url, params(查询参数字典), headers, cookies, timeout |
requests.post() |
发送 POST 请求 | url, data(表单字典), json(JSON 字典), files(上传文件), headers |
requests.put() |
发送 PUT 请求 | 通常用于更新资源,参数同 post (json 最常用) |
requests.delete() |
发送 DELETE 请求 | 通常用于删除资源 |
requests.request() |
通用请求方法 | method='GET', url, ... (可动态指定任意 HTTP 方法) |
💡 关键区别:
params: 自动拼接到 URL 后 (?key=value),用于 GET。data: 提交application/x-www-form-urlencoded格式,用于 POST 表单。json: 提交application/json格式,自动序列化字典为 JSON 字符串并设置 Header,用于 POST/PUT JSON 数据。files: 用于文件上传,需配合open(..., 'rb')使用。
2. Response 响应对象属性与方法
请求返回的对象(如 resp = requests.get(...)),包含以下核心成员:
| 成员 | 类型 | 说明与用法 |
|---|---|---|
resp.status_code |
int |
获取 HTTP 状态码 (200, 404, 500 等) |
resp.json() |
dict/list |
最常用。将响应体解析为 JSON 格式 (Python 字典或列表) |
resp.text |
str |
获取响应的文本内容 (自动根据编码解码) |
resp.content |
bytes |
获取响应的字节流 。下载图片/文件/视频时必须用此属性 |
resp.headers |
dict |
获取响应头信息 (如 Content-Type, Set-Cookie) |
resp.cookies |
RequestsCookieJar |
获取响应中的 Cookie 对象 |
resp.url |
str |
获取最终请求的 URL (若发生重定向,此为跳转后的地址) |
resp.encoding |
str |
查看或手动设置响应编码 (如 resp.encoding = 'utf-8') |
二、Session 会话管理 (保持登录状态)
场景 :当接口之间存在依赖(如:先登录获取 Token/Cookie,再调用业务接口),必须使用 Session 对象来自动维护状态。
1. 核心方法与用法 (基于图片内容)
(1) 创建 Session 对象
import requests
session = requests.Session()
(2) 使用 Session 发送请求
与普通 requests 用法一致,但自动携带该 Session 中保存的 Cookie 和 Header。
# 第一次请求:登录,Session 自动保存返回的 Cookie
res_login = session.post(url_login, json={"user": "admin", "pwd": "123"})
# 第二次请求:业务操作,自动带上登录时的 Cookie,无需手动传
res_business = session.get(url_business)
(3) 共享/更新请求头 (全局设置)
重要函数 :session.headers.update()
用于在 Session 级别统一设置 Header(如 Token),后续所有请求自动生效。
# 提取 token
token = res_login.json().get("token")
# 全局更新 Header
session.headers.update({
"Authorization": f"Bearer {token}",
"User-Agent": "Custom User Agent", # 自定义 UA
"Content-Type": "application/json"
})
(4) 关闭 Session
session.close()
三、PyMySQL 数据库操作 (数据准备与校验)
用于测试前的数据构造 (插入测试数据)和测试后的结果验证 (查询数据库落库情况)或环境清理。
1. 连接数据库 (基于图片内容)
import pymysql
conn = pymysql.connect(
host="192.168.1.100", # 数据库服务器地址
user="root", # 登录用户名
password="123456", # 密码
database="test_db", # 要连接的数据库名称
port=3306, # 端口号 (默认 3306)
charset="utf8" # 字符集 (建议 utf8 或 utf8mb4)
)
2. 获取游标与执行 SQL
cursor = conn.cursor() # 获取游标对象
# 执行单条 SQL
sql = "SELECT * FROM users WHERE id = 1"
cursor.execute(sql)
# 执行带参数的 SQL (防注入)
sql_insert = "INSERT INTO users (name, age) VALUES (%s, %s)"
cursor.execute(sql_insert, ("ZhangSan", 18))
3. 获取查询结果 (基于图片内容)
| 方法 | 说明 | 返回值示例 |
|---|---|---|
cursor.fetchone() |
获取1 行数据 | ('ZhangSan', 18) (元组) 或 None |
cursor.fetchmany(size) |
获取指定行数数据 | (元组列表) |
cursor.fetchall() |
获取全部数据 | (元组列表) |
4. 事务控制 (增删改必做)
conn.commit() # ✅ 提交事务:执行 INSERT/UPDATE/DELETE 后必须调用,否则不生效
conn.rollback() # ❌ 回滚事务:发生异常时调用,撤销操作
5. 关闭资源
cursor.close()
conn.close()
四、JSON Schema 校验 (响应结构验证)
用于严格校验接口返回的 JSON 数据结构、字段类型、必填项是否符合契约,比简单的 assert 更强大。
1. 安装
pip install jsonschema
2. 定义 Schema 规则
schema = {
"type": "object",
"required": ["code", "msg", "data"], # 必填字段
"properties": {
"code": {"type": "integer"}, # code 必须是整数
"msg": {"type": "string"}, # msg 必须是字符串
"data": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"}
}
}
}
}
3. 执行校验
import jsonschema
try:
# instance: 实际响应数据 (resp.json())
# schema: 定义的规则
jsonschema.validate(instance=response.json(), schema=schema)
print("✅ 结构校验通过")
except jsonschema.exceptions.ValidationError as e:
print(f"❌ 结构校验失败: {e.message}")
五、RPC 接口测试 (Dubbo & Telnet)
针对非 HTTP 协议(如 Dubbo)的接口测试方法。
1. Telnet 命令 (手动测试)
适用于支持 Telnet 协议的 Dubbo 服务端。
# 1. 连接
telnet <IP> <Port>
# 2. 常用命令
ls # 查看服务列表
ps # 查看提供者和消费者详情
invoke com.example.UserService.getUserById(1) # 调用方法 (直接写 Java 方法调用形式)
2. dubboclient 库 (Python 自动化)
安装 :pip install dubbo-client
核心函数:
from dubbo_client import Client
# 1. 初始化客户端 (直连提供者 IP:Port 或 注册中心)
client = Client(['192.168.1.100:20880'])
# 2. 调用远程方法 (invoke)
result = client.invoke(
interface='com.example.UserService', # 接口全限定名
method='getUserById', # 方法名
args=[1], # 参数列表
version='1.0.0', # 版本号 (可选)
group='dubbo' # 分组 (可选)
)
print(result) # 输出反序列化后的 Python 对象
六、Pytest 框架集成与数据驱动
1. 核心装饰器与断言
| 名称 | 类型 | 说明 |
|---|---|---|
@pytest.mark.parametrize() |
装饰器 | 数据驱动核心。传入参数名和参数值列表,自动生成多条用例。 |
assert |
关键字 | 断言。例:assert resp.status_code == 200 |
setup_method() |
特殊方法 | 每个 test_ 方法执行前运行 (常用于登录获取 Token)。 |
teardown_method() |
特殊方法 | 每个 test_ 方法执行后运行 (常用于清理数据)。 |
pytest.main() |
函数 | 在脚本内部启动测试。例:pytest.main(["-s", "test_api.py"]) |
2. 数据驱动示例
import pytest
import requests
@pytest.mark.parametrize("username, pwd, expect_code", [
("admin", "123456", 200),
("user", "wrong", 500),
("", "123456", 400)
])
def test_login(username, pwd, expect_code):
url = "http://api.com/login"
resp = requests.post(url, json={"username": username, "password": pwd})
assert resp.status_code == expect_code
3. JSON 文件驱动 (进阶)
将测试数据写在 .json 文件中,通过工具函数读取并传给 parametrize。
# 读取 JSON 并转换为列表
def load_data():
import json
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
return [(item['user'], item['pwd'], item['code']) for item in data]
@pytest.mark.parametrize("user,pwd,code", load_data())
def test_login_dynamic(user, pwd, code):
# 测试逻辑...
pass
七、辅助工具函数汇总
| 模块 | 函数 | 用途 |
|---|---|---|
| random | random.randint(a, b) |
生成随机整数 (如随机手机号、合同号) |
| time | time.time() |
获取当前时间戳 (浮点数) |
| 组合 | str(int(time.time())) |
生成基于时间的唯一字符串 ID |
| json | json.load(f) / json.dump() |
读取/写入 JSON 配置文件 |
| os/sys | os.getcwd() |
获取当前工作目录 (用于构建文件绝对路径) |
| 内置 | open(file, 'rb'/'wb') |
二进制读写文件 (上传/下载必备) |
🚀 综合实战流程示例
import requests
import pymysql
import pytest
import random
import time
# 1. 前置准备 (Setup)
class TestContract:
def setup_method(self):
# A. 数据库连接 (准备数据)
self.conn = pymysql.connect(host="...", user="...", password="...", database="...", charset="utf8")
self.cursor = self.conn.cursor()
# B. 登录获取 Token (Session 保持)
self.session = requests.Session()
login_data = {"user": "admin", "pwd": "123456"}
resp = self.session.post("http://api.com/login", json=login_data)
token = resp.json().get("token")
self.session.headers.update({"Authorization": f"Bearer {token}"})
def test_upload_contract(self):
# C. 构造动态数据
contract_no = f"HT{random.randint(10000, 99999)}_{int(time.time())}"
# D. 发送请求 (文件上传)
files = {"file": ("contract.pdf", open("contract.pdf", "rb"), "application/pdf")}
params = {"contractNo": contract_no}
resp = self.session.post("http://api.com/upload", params=params, files=files)
# E. 断言
assert resp.status_code == 200
assert resp.json().get("code") == 200
# F. 数据库校验 (验证是否落库)
sql = "SELECT count(*) FROM contracts WHERE no = %s"
self.cursor.execute(sql, (contract_no,))
count = self.cursor.fetchone()[0]
assert count == 1, "数据未成功写入数据库"
def teardown_method(self):
# G. 清理资源
self.conn.rollback() # 测试数据回滚,保持环境干净
self.cursor.close()
self.conn.close()
self.session.close()
if __name__ == "__main__":
pytest.main(["-s", __file__])