接口测试理论

Content-Type:请求体数据类型

text/html:HTML 格式

text/plain:纯文本格式

image/jpeg:jpg 图片格式

application/json:JSON 数据格式

application/x-www-form-urlencoded:表单默认的提交数据格式

multipart/form-data:在表单中进行文件上传时使用

接口测试流程:

  • 需求分析:明确接口要实现的业务目标
  • 接口文档解析:明确接口功能、请求方式(GET/POST 等)、入参 / 出参格式、响应码、异常规则
  • 设计测试用例:覆盖正常场景(参数合法、功能达标)、异常场景(参数缺失 / 格式错误 / 越权)、边界场景(参数极值)。
  • 脚本开发:编写自动化测试脚本
  • 执行及缺陷跟踪:对应之前的 "接口调用验证 + 缺陷管理"
  • 生成测试报告:和之前的 "报告输出" 一致
  • 接口自动化持续集成(可选):(补充了自动化测试的后续维护环节)

Postman断言

1. 验证响应状态码(最常用)
javascript 复制代码
// 验证接口返回200成功
pm.test("响应状态码为200", function () {
    pm.response.to.have.status(200);
});

// 验证接口返回指定错误状态码(比如404/401)
pm.test("响应状态码为401(未授权)", function () {
    pm.response.to.have.status(401);
});
2. 验证响应时间(性能初筛)
javascript 复制代码
// 验证接口响应时间≤2000ms(可根据需求调整)
pm.test("响应时间≤2000ms", function () {
    pm.expect(pm.response.responseTime).to.be.below(2000);
});
3. 验证JSON响应体的业务字段(核心)
javascript 复制代码
// 先解析响应体为JSON对象
const resData = pm.response.json();

// 验证业务状态码(比如code=0代表成功)
pm.test("业务状态码为0", function () {
    pm.expect(resData.code).to.eql(0); // eql是严格相等
});

// 验证提示信息(比如message为"操作成功")
pm.test("提示信息为操作成功", function () {
    pm.expect(resData.message).to.eql("操作成功");
});

// 验证返回数据非空(比如data字段存在且不为空)
pm.test("返回数据data非空", function () {
    pm.expect(resData.data).to.exist.and.not.be.empty;
});
4. 验证JSON数组的长度/内容
javascript 复制代码
const resData = pm.response.json();

// 验证列表数据长度≥1(比如查询接口返回至少1条数据)
pm.test("列表数据至少有1条", function () {
    pm.expect(resData.data.list).to.be.an("array").and.have.lengthOf.at.least(1);
});

// 验证数组中某条数据的字段值(比如第一条数据的id=1001)
pm.test("列表第一条数据的id为1001", function () {
    pm.expect(resData.data.list[0].id).to.eql(1001);
});
5. 验证响应体包含特定字符串
javascript 复制代码
// 适用于非JSON响应(比如HTML/文本),验证包含指定内容
pm.test("响应体包含'成功'关键词", function () {
    pm.response.to.have.body("成功");
    // 模糊匹配用:pm.response.to.have.body.match(/成功/);
});
响应头验证
javascript 复制代码
// 验证响应头的Content-Type(比如JSON格式)
pm.test("响应格式为JSON", function () {
    pm.response.to.have.header("Content-Type", "application/json; charset=utf-8");
});

// 验证响应头包含指定字段(比如Token)
pm.test("响应头包含Token字段", function () {
    pm.expect(pm.response.headers.has("Token")).to.be.true;
});
业务逻辑断言
javascript 复制代码
const resData = pm.response.json();

// 验证分页参数匹配(比如当前页page=1,每页条数size=10)
pm.test("分页参数符合预期", function () {
    pm.expect(resData.data.page).to.eql(1);
    pm.expect(resData.data.size).to.eql(10);
});

// 验证字段类型(比如id是数字类型)
pm.test("id字段为数字类型", function () {
    pm.expect(resData.data.id).to.be.a("number");
});

requests发送请求

请求方法格式:

python 复制代码
requests.请求方法(url, params=None, data=None, json=None, headers=None)

说明:

  • 请求方法: get/post/put/delete
  • url: 请求的url地址
  • params: 请求查询参数(URL 后? key=value
  • data: 表单参数
  • json: json参数
  • headers: 请求头参数, 比如 token

这些参数都使用python字典传输

Get方式传参:

如果接口是路径参数:

python 复制代码
user_id = 1  
url = f"{baseUrl}/users/{user_id}" 
response = requests.get(url)

如果接口是查询参数:

python 复制代码
# 如果后端API设计为 /users?user_id=1
url = f"{baseUrl}/users"
response = requests.get(url, params={"user_id": 1})  # ✅ params必须是字典

post 的参数

python 复制代码
# data 是表单参数  
response=requests.post(url=baseUrl+"/users",data=userInfo)  
# 正确:使用json参数发送JSON数据  
response = requests.post(url=baseUrl + "/users", json=userInfo)
基本用法
python 复制代码
import requests
# 发送GET请求
r = requests.get('https://www.baidu.com')
# 查看响应状态码
print(r.status_code)
# 查看响应内容
print(r.text)
# 查看Cookies
print(r.cookies)
# 请求的URL
print(r.url)
# 请求历史                     
print(r.history)
# 响应头                 
print(r.headers)                 
设置请求头:
python 复制代码
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0',
    'my-test': 'Hello'
}
r = requests.get('http://httpbin.org/get', headers=headers)
文件上传:
python 复制代码
files = {'file': open('favicon.ico', 'rb')}
r = requests.post('http://httpbin.org/post', files=files)
获取Cookies:
python 复制代码
r = requests.get('https://www.baidu.com')
print(r.cookies)
for key, value in r.cookies.items():
    print(key + '=' + value)

使用Cookies维持登录状态:

python 复制代码
headers = {
    'Cookie': 'your_cookie_string',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:95.0) Gecko/20100101 Firefox/95.0'
}
r = requests.get('https://www.zhihu.com', headers=headers)
会话维持(Session)
python 复制代码
s = requests.Session()
s.get('http://httpbin.org/cookies/set/number/123456789')
r = s.get('http://httpbin.org/cookies')
print(r.text)
总超时时间
python 复制代码
r = requests.get('https://www.taobao.com', timeout=1)
分别指定连接和读取超时
python 复制代码
r = requests.get('https://www.taobao.com', timeout=(5, 30))
永久等待
python 复制代码
r = requests.get('https://www.taobao.com', timeout=None)

PyMysql操作

1.安装pymysql:
bash 复制代码
pip install PyMySQL  
2.数据库操作的固定流程
python 复制代码
# 1. 导包(必须第一步)
import pymysql

# 2. 创建数据库连接(核心参数PPT已给出)
conn = pymysql.connect(
    host="数据库地址",  # 示例:211.103.136.244(PPT测试地址)
    port=端口号,       # 示例:7061(默认3306,非默认必须写)
    user="用户名",     # 示例:student
    password="密码",   # 示例:iHRM_student_2021
    database="数据库名",# 示例:test_db
    charset="utf8"     # 固定值,避免中文乱码
)

# 3. 获取游标(执行SQL的工具)
cursor = conn.cursor()

# 4. 执行SQL操作(查询/增删改,核心步骤)
# TODO:这里替换成具体SQL

# 5. 释放资源(必须做,避免占用连接)
cursor.close()  # 先关游标
conn.close()    # 再关连接
3. 核心操作:增删改查
(1)查询操作(最常用,用于"数据校验")
python 复制代码
# 示例:验证"新增图书"接口后,查询t_book表是否有该图书
import pymysql

conn = pymysql.connect(host="211.103.136.244", port=7061, user="student", password="iHRM_student_2021", database="test_db", charset="utf8")
cursor = conn.cursor()

# 执行查询SQL
sql = "select * from t_book where title='西游记'"
cursor.execute(sql)  # 执行SQL

# 3种获取结果的方式
result1 = cursor.fetchone()  # 取结果集的第1条(最常用,验证单条数据)
result2 = cursor.fetchmany(2) # 取指定条数(比如前2条)
result3 = cursor.fetchall()   # 取所有结果(比如验证批量新增)

print("查询结果:", result1)  # 输出:(5, '西游记', '1986-01-01', ...)

cursor.close()
conn.close()
(2)增删改操作(用于"造数据/清环境")

这三类操作需要事务提交,否则操作无效,且要处理异常

python 复制代码
import pymysql
import traceback  # 捕获异常

conn = pymysql.connect(host="211.103.136.244", port=7061, user="student", password="iHRM_student_2021", database="test_db", charset="utf8")
cursor = conn.cursor()

try:
    # 1. 新增数据(造测试数据)
    sql_insert = "insert into t_book(id, title, pub_date) values(5, '西游记', '1986-01-01')"
    # 2. 更新数据(比如修改测试数据状态)
    sql_update = "update t_book set `read` = `read` + 1 where title='西游记'"
    # 3. 删除数据(清理测试环境)
    sql_delete = "delete from t_book where title='西游记'"

    # 执行其中一个操作(按需选)
    cursor.execute(sql_insert)
    conn.commit()  # 必须提交事务,否则数据不生效
    print("操作成功")

except Exception as e:
    conn.rollback()  # 出错时回滚,避免数据混乱
    print("操作失败:", traceback.format_exc())  # 打印异常信息

finally:
    # 无论成功失败,都关闭资源
    cursor.close()
    conn.close()
4. 重复写连接/关闭代码太繁琐,封装成工具类:
python 复制代码
import pymysql
import traceback

class DBUtil:
    # 1. 获取数据库连接(类方法,无需实例化)
    @classmethod
    def get_conn(cls):
        conn = None
        try:
            conn = pymysql.connect(
                host="211.103.136.244",
                port=7061,
                user="student",
                password="iHRM_student_2021",
                database="test_db",
                charset="utf8"
            )
        except Exception as e:
            print("连接数据库失败:", e)
        return conn

    # 2. 关闭连接(统一释放资源)
    @classmethod
    def close_conn(cls, conn, cursor):
        if cursor:
            cursor.close()
        if conn:
            conn.close()

    # 3. 查询一条数据(验证接口常用)
    @classmethod
    def get_one(cls, sql):
        conn = cls.get_conn()
        cursor = conn.cursor()
        result = None
        try:
            cursor.execute(sql)
            result = cursor.fetchone()
        except Exception as e:
            print("查询失败:", traceback.format_exc())
        finally:
            cls.close_conn(conn, cursor)
        return result

    # 4. 执行增删改(造数据/清环境常用)
    @classmethod
    def uid_db(cls, sql):
        conn = cls.get_conn()
        cursor = conn.cursor()
        try:
            cursor.execute(sql)
            conn.commit()
            return True  # 操作成功返回True
        except Exception as e:
            conn.rollback()
            print("执行失败:", traceback.format_exc())
            return False  # 失败返回False
        finally:
            cls.close_conn(conn, cursor)

# 直接调用
if __name__ == "__main__":
    # 查数据(验证接口)
    sql_select = "select * from t_book where id=5"
    print(DBUtil.get_one(sql_select))

    # 删数据(清环境)
    sql_delete = "delete from t_book where id=5"
    DBUtil.uid_db(sql_delete)
6. 场景1:接口测试数据校验
python 复制代码
import requests
from DBUtil import DBUtil  # 导入上面封装的工具类

# 1. 调用新增图书接口
url = "https://your-api.com/book/add"
headers = {"Content-Type": "application/json"}
json_data = {"id": 5, "title": "西游记", "pub_date": "1986-01-01"}
requests.post(url=url, headers=headers, json=json_data)

# 2. 数据库校验
sql = "select title from t_book where id=5"
result = DBUtil.get_one(sql)

# 3. 断言(测开核心:验证接口是否真的生效)
assert result[0] == "西游记", f"数据校验失败,实际结果:{result}"
print("接口测试通过")
7. 场景2:批量造测试数据(实习效率神器)
python 复制代码
from DBUtil import DBUtil

for i in range(10, 20):  # id从10到19
    sql = f"insert into t_book(id, title, pub_date) values({i}, '测试图书{i}', '2024-01-01')"
    DBUtil.uid_db(sql)
print("10条测试数据创建完成")

日志

常见日志级别(从低到高)
级别 含义 适用场景
DEBUG 调试级(最详细),打印代码调试信息(如变量值、函数调用细节) 开发/测试阶段调试代码
INFO 信息级,记录系统核心运行步骤(如"接口请求发起""脚本执行开始") 监控正常流程
WARNING 警告级,潜在错误(不影响系统当前运行) 如"参数格式不规范但已兼容"
ERROR 错误级,程序出现BUG(功能异常) 如"接口调用失败""数据查询报错"
CRITICAL 严重错误级,系统无法继续运行 如"数据库连接失败""核心服务崩溃"

当为程序指定一个日志级别后,程序会记录所有日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的日志信息

日志文件需要避免过大(难以打开),建议 按时间切割(如每日生成1个日志文件),并限制备份数量(如保留3天的日志)

实操:用Python Logging收集日志

组件 作用
Logger(日志器) 核心入口,负责发起日志记录请求,设置全局日志级别
Handler(处理器) 决定日志输出位置(控制台/文件),支持同时绑定多个处理器(如既打控制台又存文件)
Formatter(格式化器) 定义日志的显示格式(如包含时间、级别、代码位置、日志内容)
python 复制代码
# logger_utils.py(日志工具类,直接复制复用)  
import logging  
import logging.handlers  
import os  
  
# 1. 配置日志路径(可按需修改)  
LOG_DIR = "./logs"  
if not os.path.exists(LOG_DIR):  
    os.makedirs(LOG_DIR)  
  
# 2. 创建日志器(避免重复输出)  
logger = logging.getLogger("auto_test_logger")  # 给日志器命名,避免全局冲突  
logger.setLevel(logging.DEBUG)  
  
# 3. 仅在无处理器时添加(解决重复打印问题)  
if not logger.handlers:  
    # 控制台处理器(只显示≥INFO级别,避免调试信息刷屏)  
    console_handler = logging.StreamHandler()  
    console_handler.setLevel(logging.INFO)  
  
    # 文件处理器(按时间切割,保留3天)  
    file_handler = logging.handlers.TimedRotatingFileHandler(  
        filename=os.path.join(LOG_DIR, "auto_test.log"),  
        when='MIDNIGHT',  
        interval=1,  
        backupCount=3,  
        encoding='utf-8'  # 解决中文乱码  
    )  
    file_handler.setLevel(logging.DEBUG)  
  
    # 日志格式(包含关键信息,便于排查)  
    fmt = "%(asctime)s - %(levelname)s - [%(filename)s:%(funcName)s:%(lineno)d] - %(message)s"  
    formatter = logging.Formatter(fmt)  
  
    # 绑定格式和处理器  
    console_handler.setFormatter(formatter)  
    file_handler.setFormatter(formatter)  
    logger.addHandler(console_handler)  
    logger.addHandler(file_handler)  
  
# 对外暴露logger,供其他文件导入  
if __name__ == "__main__":  
    # 测试日志是否正常输出  
    logger.debug("工具类调试信息")  
    logger.info("工具类初始化成功")
关键API说明
(1)Logger日志器
  • 创建:logging.getLogger()(全局唯一,重复调用返回同一对象)
  • 设置级别:logger.setLevel(logging.XXX)(XXX为DEBUG/INFO等)
  • 记录日志:logger.debug(msg)/logger.info(msg)/logger.warning(msg)等(方法名小写)
(2)Handler处理器
  • 控制台输出:logging.StreamHandler()
  • 普通文件输出:logging.FileHandler(filename)(不切割,文件会越来越大)
  • 按时间切割文件:logging.handlers.TimedRotatingFileHandler()(需导包logging.handlers
  • 绑定格式化器:handler.setFormatter(formatter)
(3)Formatter格式化器
  • 常用格式参数:
    • %(asctime)s:当前时间(默认格式:2003-07-08 16:49:45,896)
    • %(levelname)s:日志级别(如DEBUG、INFO)
    • %(filename)s:产生日志的文件名
    • %(funcName)s:产生日志的函数名
    • %(lineno)d:产生日志的代码行号
    • %(message)s:日志核心内容(自定义消息)

接口自动化框架

项目结构

txt 复制代码
apiTestFramework
├── api                  # 封装被测系统接口
├── scripts              # 测试用例脚本
├── data                 # 测试数据文件  
├── report               # 测试报告
├── common               # 通用工具类
├── logs                 # 日志文件
├── config.py            # 项目配置信息
├── run_suite.py         # 执行测试套件的入口
├── requirements.txt     # 依赖库清单
├── pytest.ini           # pytest配置文件
└── docs                 # 文档目录

json schema

用于校验json的格式

在线校验工具:

python 复制代码
SCHEMA = {  
    "$schema": "http://json-schema.org/draft-07/schema#",  
    "type": "object",  
    "properties": {  
        "username": {"type": "string", "minLength": 2},  
        "age": {"type": "integer", "minimum": 18, "maximum": 60},  
        "gender": {"type": "string", "enum": ["male", "female"]}  
    },  
    "required": ["username", "age", "gender"]  
}
关键字 描述
type 限定JSON元素的数据类型
properties 定义JSON对象中各字段的校验规则(仅type为object时使用)
required 指定JSON对象中必须存在的字段(数组格式,元素唯一)
const 强制字段值等于指定内容(精准匹配)
pattern 用正则表达式约束字符串类型字段

在python中校验:

安装依赖库:

bash 复制代码
pip install jsonschema

核心函数:

python 复制代码
jsonschema.validate(instance=待校验JSON数据, schema=校验规则Schema)
  • instance:接口返回的实际JSON数据(如response.json()
  • schema:提前定义的JSON Schema校验规则

异常处理:

  • jsonschema.exceptions.SchemaError:Schema规则本身不合法时抛出
  • jsonschema.exceptions.ValidationError:JSON数据不符合Schema规则时抛出

CI/CD

持续集成目的:快速迭代 保持高质量

持续集成

开发人员提交新代码后,立刻构建,部署到「测试环境」,执行测试并反馈

持续交付

在持续集成的基础上,将集成后的代码部署到「类生产环境」

持续部署

在持续交付的基础上,自动部署到生产环境加粗样式

相关推荐
杭州杭州杭州2 小时前
Docker
运维·docker·容器
cyforkk2 小时前
13、Java 基础硬核复习:泛型(类型安全)的核心逻辑与面试考点
java·开发语言·面试
VT.馒头2 小时前
【力扣】2694. 事件发射器
前端·javascript·算法·leetcode·职场和发展·typescript
试着3 小时前
【huawei】机试
华为·面试·机试·手搓代码
编程彩机3 小时前
互联网大厂Java面试:从分布式事务到微服务优化的技术场景解读
java·spring boot·redis·微服务·面试·kafka·分布式事务
编程彩机3 小时前
互联网大厂Java面试:从Spring WebFlux到分布式事务的技术场景解析
java·微服务·面试·分布式事务·spring webflux
kogorou0105-bit3 小时前
前端设计模式:发布订阅与依赖倒置的解耦之道
前端·设计模式·面试·状态模式
练习时长一年4 小时前
LeetCode热题100(颜色分类)
算法·leetcode·职场和发展