第一部分:接口自动化测试框架详解

一、框架整体设计

1.1 技术栈
  • Python 3.8+ - 核心语言
  • pytest - 测试执行框架
  • requests - HTTP 请求发送
  • pymysql - MySQL 数据库操作
1.2 项目结构
auto_api_test/
├── config/                     # 配置目录
│   ├── settings.py            # 核心配置(数据库、环境、常量)
├── utils/                      # 工具模块
│   ├── data_driver.py         # 数据驱动(参数注入)
│   ├── run_case_util.py       # 测试执行
│   ├── case_util.py          # 用例加载
│   ├── request_util.py        # HTTP 请求发送
│   ├── assert_util.py         # 断言工具
│   ├── extract_util.py        # 关联值提取
│   ├── replace_util.py        # 参数替换
│   ├── mysql_util.py          # 数据库操作
│   ├── logger_util.py         # 日志工具
│   └── exceptions.py          # 自定义异常
├── testcase/                   # 测试用例目录
├── upload_files/               # 上传文件目录
├── log/                        # 日志目录
└── report/                     # 测试报告目录
1.3 核心执行流程
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  加载用例   │ ──▶ │  参数注入   │ ──▶ │  关联替换   │
│  (数据库)   │     │ (数据驱动)  │     │ (动态参数)  │
└─────────────┘     └─────────────┘     └─────────────┘
                                              │
                                              ▼
┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  生成报告   │ ◀── │  执行断言   │ ◀── │  发送请求   │
│  (统计汇总) │     │ (多维验证)  │     │ (requests)  │
└─────────────┘     └─────────────┘     └─────────────┘
1.4 核心模块解析
1.4.1 用例加载模块 (case_util.py)
class RdTestCase:
    """测试用例读取工具"""
    
    def load_param_cases(self, case_table, web):
        """加载参数化用例,自动展开每条参数组合"""
        base_cases = self.load_run_cases(case_table, web)
        param_cases = []
        
        for case in base_cases:
            param_set_id = case.get('param_set_id')
            if not param_set_id:
                param_cases.append(case)
            else:
                # 从 test_data 表获取参数组合
                param_combinations = data_driver.get_data_set(param_set_id, web)
                for i, params in enumerate(param_combinations):
                    # 为每个参数组合生成独立用例
                    new_case = case.copy()
                    new_case['id'] = f"{original_id}_{i + 1}"
                    new_case['title'] = f"{case_title} [参数{i + 1}]"
                    # 注入参数到用例各字段
                    new_case = data_driver.inject_params_to_case(new_case, params)
                    param_cases.append(new_case)
        
        return param_cases

核心能力:

  • 从数据库 case_list 表加载用例
  • 自动识别 param_set_id 关联参数化数据
  • 将单条用例展开为多条参数化用例
1.4.2 数据驱动模块 (data_driver.py)
class DataDriver:
    """数据驱动工具类 - 支持从数据库获取测试数据并注入到用例中"""
    
    def inject_params_to_case(self, case: Dict, params: Dict) -> Dict:
        """将参数注入到用例中 - 支持 path_params、query_params、headers 和 request_body"""
        
        # 1. 处理 URL 路径中的参数
        new_case['url'] = self._replace_in_value(new_case['url'], params)
        
        # 2. 处理路径参数 (path_params)
        new_case['path_params'] = self._replace_in_value(new_case['path_params'], params)
        
        # 3. 处理查询参数 (query_params)
        new_case['query_params'] = self._replace_in_value(new_case['query_params'], params)
        
        # 4. 处理请求头 (headers)
        new_case['headers'] = self._replace_in_value(new_case['headers'], params)
        
        # 5. 处理请求体 (request_body)
        if request_type == 'json':
            new_case['request_body'] = self._process_json_body(new_case['request_body'], params)
        
        return new_case

参数注入规则:

  • ${param_name} 占位符自动替换为参数值
  • 支持递归替换嵌套 JSON 结构
  • 支持 URL、headers、params、body 全字段覆盖
1.4.3 关联值提取模块 (extract_util.py)
class RelationExtractor:
    """关联值提取器"""
    
    @staticmethod
    def extract_values(response_data, relation_config):
        """
        从前一个接口的响应中提取值供后续用例使用
        
        配置格式:动态名=数据源。路径
        示例:token=body.data.access_token;user_id=body.data.user.id
        """
        relations = relation_config.split(';')
        for config in relations:
            dynamic_name, field_path = config.split('=')
            value = self._extract_single_value(response_data, field_path)
            setattr(DynamicParam, dynamic_name, value)

支持的数据源:

  • body - 响应体数据
  • headers - 响应头
  • cookies - Cookie 数据

支持的路径格式:

  • 普通路径:body.data.user.id
  • 数组索引:body.data.products[0].id
  • 负索引:body.data.products[-1].id (最后一个)
  • JSONPath:$.body.data[0].id
1.4.4 断言模块 (assert_util.py)
class AssertUtil:
    """断言工具类"""
    
    @classmethod
    def assert_response(cls, case: Dict, response_data: Dict) -> bool:
        """执行响应断言"""
        expected_config = cls._parse_expected(case)
        actual = {
            'code': response_data.get('code'),  # HTTP 状态码
            'body_code': body.get('code'),      # 业务状态码
            'msg': body.get('msg'),
            'data': body.get('data', {})
        }
        
        # 支持单条断言和多条断言两种模式
        if 'assertions' not in expected_config:
            errors = cls._assert_single(expected_config, actual)
        else:
            errors = cls._assert_multi(expected_config, actual)

特殊标记:

  • @not_null@ - 验证字段不为空
  • @ignore@ - 忽略该字段
1.4.5 参数替换模块 (replace_util.py)
class RelationReplacer:
    """关联值替换器 - 处理 ${} 占位符"""
    
    # 动态值生成器
    DYNAMIC_GENERATORS = {
        'timestamp()': lambda: str(int(time.time())),
        'datetime()': lambda: datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        'date()': lambda: datetime.now().strftime('%Y-%m-%d'),
        'uuid()': lambda: str(uuid.uuid4()),
        'time_ms()': lambda: str(int(time.time() * 1000)),
        'today()': lambda: datetime.now().strftime('%Y%m%d'),
    }

占位符类型:

  1. 动态参数 - ${token} 从 DynamicParam 获取
  2. 嵌套路径 - ${user.id} 从 DynamicParam.user.id 获取
  3. 动态生成 - ${timestamp()} 实时生成时间戳

二、数据库管理用例 vs YAML 文件管理用例对比

2.1 数据库管理方案(当前框架)

表结构概览:

-- case_list 表:存储测试用例
CREATE TABLE case_list (
    id INT PRIMARY KEY,
    web VARCHAR(20),        -- 测试环境
    module VARCHAR(50),     -- 所属模块
    title VARCHAR(200),     -- 用例标题
    url VARCHAR(500),       -- 接口 URL
    method VARCHAR(10),     -- 请求方法
    headers TEXT,           -- JSON 格式
    request_body TEXT,      -- JSON 格式
    request_type VARCHAR(20),
    relation VARCHAR(500),  -- 关联值提取配置
    expected TEXT,          -- JSON 格式断言配置
    param_set_id VARCHAR(50), -- 关联参数数据集
    isexec INT,             -- 是否执行 (1/0)
    position INT            -- 执行顺序
);

-- test_data 表:存储参数化数据
CREATE TABLE test_data (
    id INT PRIMARY KEY,
    data_set_id VARCHAR(50),  -- 关联用例 ID
    web VARCHAR(20),
    param_values JSON,        -- 参数组合 {"key":"value"}
    description VARCHAR(200),
    is_active INT,
    sort_id INT
);
2.2 YAML 管理方案(对比方案)

典型 YAML 用例格式:

# test_login.yaml
- id: 001
  title: 登录成功
  url: /auth/login
  method: POST
  headers:
    Content-Type: application/json
  request_body:
    username: admin
    password: "123456"
  expected:
    code: 0
    msg: 成功

- id: 002
  title: 登录失败 - 密码错误
  url: /auth/login
  method: POST
  request_body:
    username: admin
    password: wrong
  expected:
    code: 1001

典型 YAML 参数化格式:

# test_login_data.yaml
test_data:
  - username: admin
    password: "123456"
    expected_code: 0
    
  - username: user001
    password: "password123"
    expected_code: 0
    
  - username: admin
    password: wrong
    expected_code: 1001
2.3 详细对比分析
对比维度数据库管理方案YAML 文件管理方案
用例维护
新增用例在线编辑,即时生效需要修改文件 + Git 提交
修改用例直接更新数据库记录需要修改文件 + 版本控制
批量操作SQL 批量更新脚本批量处理
版本追溯需额外日志表记录Git 天然支持
参数化管理
参数配置独立 test_data 表,与用例分离参数内嵌在用例文件或独立 YAML
参数复用多条用例共享同一 data_set_id需要引用外部文件
动态调整运行时从数据库加载最新参数需要重新读取文件
协作效率
多人协作需要数据库权限管理,可能冲突Git 分支 + PR 流程
非技术人员可通过界面工具维护用例需要了解 YAML 语法
代码评审需额外审核流程通过 PR 自然完成
执行效率
加载速度需要数据库查询 (可缓存优化)本地文件读取更快
并发安全数据库连接需管理文件读取无锁竞争
可维护性
可读性JSON 字段需格式化查看YAML 天然可读性好
调试难度需查询数据库确认配置文件内容直观可见
批量修改SQL 语句一次性完成需要脚本处理多个文件
2.4 选择数据库方案的理由

当前框架选择 MySQL 存储的核心原因:

  1. 动态性需求高

    • 测试用例频繁变更(敏捷开发迭代快)
    • 参数化数据需要经常调整
    • 数据库方案支持"修改即生效",无需重启或重新加载
  2. 参数化设计

    • case_list 与 test_data 分离,符合数据库范式
    • 多条用例可共享同一参数数据集
    • 参数组合可独立管理和启用/禁用
  3. 环境隔离

    • web 字段区分测试环境
    • 不同环境用例可独立配置
    • 避免多套配置文件
  4. 用例录入便捷

    • 使用apifox后置脚本和数据库操作,手动测试时将用例直接保存至数据库用例表,无需二次编写测试用例

三、优化方向

  1. CI/CD 集成 - 自动化测试流水线
  2. 版本控制 - 用例变更追溯和恢复
  3. 性能优化 - 缓存机制和批量加载
  4. 智能断言 - Schema 验证、数据库验证等
  5. 测试数据工厂 - 自动化生成测试数据
  6. 完善框架机制- 重试、跳过、超时、清理测试数据等机制

第二部分:利用 Skill 自动生成参数化测试数据

一、为什么需要自动化生成测试数据

传统测试数据准备的痛点:

  1. 手动录入效率低

    • 每条参数组合都需要手动填写
    • 大量重复性劳动(如姓名、手机号、邮箱等)
    • 容易出错(复制粘贴错误、格式错误)
  2. 数据真实性差

    • 随意填写的测试数据不符合业务规则
    • 缺少有效的数据关联(如部门 ID 对应真实的部门)
    • 无法覆盖边界值场景
  3. 维护成本高

    • 接口字段变更后,测试数据需要手动同步
    • 新增字段需要逐条补充数据
    • 测试人员需要理解接口文档才能准备数据

二、Skill 触发方式与使用流程

2.1 触发关键词

当用户在对话中输入关键词时,自动触发测试数据生成流程:

示例对话:

用户:准备测试数据
AI:好的,我将为当前接口的测试用例生成参数化测试数据...
2.2 完整执行流程
Step 1: 识别用户需求
   │
   ▼
Step 2: 查找/创建接口文档缓存
   │
   ├─ 缓存存在 → 读取 api_doc_cache/{url_hash}.json
   └─ 缓存不存在 → 调用 Apifox MCP 读取接口文档 → 保存到缓存
   │
   ▼
Step 3: 分析接口请求参数 Schema
   │
   ├─ 字段类型分析(string/integer/boolean...)
   ├─ 格式约束分析(email/phone/date...)
   ├─ 边界值分析(maxLength/minimum/enum...)
   └─ 必填项识别(required 字段)
   │
   ▼
Step 4: 分类处理字段
   │
   ├─ 关联数据(re 前缀)→ 保持 ${reXxx} 占位符
   └─ 业务数据(其他)→ AI 生成真实数据值
   │
   ▼
Step 5: 生成 SQL 文件
   │
   ├─ 输出:<原文件名>_with_data.sql
   └─ 包含:INSERT INTO test_data ... 语句
   │
   ▼
Step 6: 输出数据映射表
   │
   └─ 终端显示生成的数据详情

三、数据处理规则详解

3.1 两类数据的识别与处理

Skill 会自动将测试数据分为两类,采用不同的处理策略:

类别识别规则处理方式示例
关联数据re 前缀的字段名保持占位符,从上游接口获取reUserId → ${reUserId}
业务数据其他所有占位符AI 生成真实数据username → 张三

关联数据的识别逻辑:

def classify_field(field_name):
    """判断字段是否为关联数据"""
    if field_name.lower().startswith('re'):
        return 'relation'  # 关联数据,保持占位符
    else:
        return 'business'  # 业务数据,生成真实值

典型场景:

// 接口 A:创建用户(上游)
{
  "username": "admin",
  "password": "123456"
}
// 响应:
{
  "userId": 1001  // 需要提取的关联值
}

// 接口 B:查询用户详情(下游)
{
  "userId": "${reUserId}"  // 关联数据,保持占位符
  "detail": "用户详情"     // 业务数据,生成真实值
}
3.2 关联值的传递机制
# 上游接口提取关联值
class RelationExtractor:
    @staticmethod
    def extract_values(response_data, relation_config):
        # relation 配置:reUserId=body.data.userId
        extracted = {"reUserId": 1001}
        # 存入 DynamicParam
        setattr(DynamicParam, 'reUserId', 1001)

# 下游接口使用关联值
class RelationReplacer:
    @staticmethod
    def replace_relations(data):
        # ${reUserId} 从 DynamicParam 获取
        # 替换后:{"userId": 1001}

四、AI 生成规则与字段类型映射

4.1 基础类型生成规则
类型format生成规则示例
stringdate日期格式2026-04-13
stringdate-time日期时间2026-04-13 10:30:00
stringemail邮箱格式test@example.com
stringphone/mobile手机号13812345678
stringuri/url网址https://example.com
string无普通字符串根据字段名生成
integerint32/int64整数1001、2026
numberfloat/double浮点数99.99
boolean-布尔值true/false
4.2 业务字段生成规则
字段类型生成规则示例
姓名真实中文姓名张伟、李娜、王芳
手机号中国大陆 11 位13812345678、15987654321
邮箱常用域名test@qq.com、user@163.com
时间合理日期2026-04-10、2026-05-15
介绍/备注500 字内业务描述根据业务场景生成
URL有效网址https://example.com/page1
4.3 边界值处理
约束类型处理方式示例
maxLength生成不超过最大长度的字符串maxLength: 20 → 张三 (2 字符)
minLength生成不小于最小长度的字符串minLength: 5 → 张_伟_001 (6 字符)
maximum生成不超过最大值的数值maximum: 100 → 85
minimum生成不小于最小值的数值minimum: 0 → 25
required必填字段必须生成值不生成 null/undefined
enum必须是枚举值之一enum: [1,2,3] → 2
4.4 接口文档 Schema 解析示例

OpenAPI Schema 定义:

{
  "type": "object",
  "properties": {
    "username": {
      "type": "string",
      "minLength": 3,
      "maxLength": 20,
      "description": "用户名"
    },
    "email": {
      "type": "string",
      "format": "email",
      "description": "邮箱地址"
    },
    "age": {
      "type": "integer",
      "minimum": 18,
      "maximum": 100
    },
    "phone": {
      "type": "string",
      "pattern": "^1[3-9]\\d{9}$"
    },
    "status": {
      "type": "integer",
      "enum": [0, 1, 2]
    }
  },
  "required": ["username", "email"]
}

AI 生成的数据:

{
  "username": "张伟_001",
  "email": "zhangwei@qq.com",
  "age": 28,
  "phone": "13812345678",
  "status": 1
}

五、接口文档缓存机制

5.1 缓存目录结构
auto_api_test/
└── api_doc_cache/
    ├── _sah_ic_classInfo.json      # 接口缓存 1
    ├── _auth_login.json            # 接口缓存 2
    └── _hg_companyInfo_add.json    # 接口缓存 3
5.2 缓存文件命名规则

缓存文件采用 <url_path_hash>.json 格式:

接口 URL缓存文件名
/sah/ic/classInfo_sah_ic_classInfo.json
/auth/login_auth_login.json
/hg/companyInfo/add_hg_companyInfo_add.json
5.3 缓存文件内容结构
{
  "url": "/sah/ic/classInfo",
  "method": "POST",
  "cache_time": "2026-04-13 10:30:00",
  "request_schema": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "maxLength": 50,
        "description": "班级名称"
      },
      "startDate": {
        "type": "string",
        "format": "date",
        "description": "开班日期"
      },
      "teacherId": {
        "type": "integer",
        "description": "教师 ID"
      }
    },
    "required": ["name", "startDate"]
  },
  "response_schema": {
    "code": {"type": "integer"},
    "msg": {"type": "string"},
    "data": {"type": "object"}
  }
}
5.5 Apifox MCP 集成

准备数据前的缓存检查流程:

1. 根据用例的 url 和 method 查找 api_doc_cache/ 目录
   │
   ├─ 缓存存在 
   │   └─→ 直接读取缓存中的字段类型定义
   │
   └─ 缓存不存在
       └─→ 调用 Apifox MCP 读取接口文档
           └─→ 保存到缓存文件
               └─→ 读取缓存中的字段类型定义

六、输出结果与数据验证

6.1 SQL 文件输出

输出格式:

-- 文件名:test_case_001_with_data.sql
-- 生成时间:2026-04-13 10:30:00

INSERT INTO test_data (data_set_id, web, param_values, description, is_active, sort_id) VALUES
('case_list_001', 'test', '{"username": "张伟", "email": "zhangwei@qq.com", "age": 28}', '测试用例 001 - 参数组合 1', 1, 1),
('case_list_001', 'test', '{"username": "李娜", "email": "lina@163.com", "age": 32}', '测试用例 001 - 参数组合 2', 1, 2),
('case_list_001', 'test', '{"username": "王芳", "email": "wangfang@sina.com", "age": 25}', '测试用例 001 - 参数组合 3', 1, 3);

七、总结

8.1 Skill 核心价值
传统方式Skill 自动化
手动录入数据AI 自动生成
数据质量依赖个人经验AI 基于 Schema 智能生成
8.2 优化方向
  1. 多接口联动

    • 自动分析接口依赖关系
    • 生成完整的测试场景数据链
  2. 智能场景覆盖

    • 基于历史测试数据推荐参数组合
    • 自动生成边界值和异常场景数据
  3. AI 增强

    • 根据接口描述生成更贴合业务的测试数据
    • 支持自然语言指令定制数据规则
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐