# AI + MCP 驱动的 CRM 接口自动化测试框架设计与实践

> 把 YAML 数据驱动、多账号会话隔离、LLM 用例生成、企业微信通知熔进一个测试框架,让测试工程师专注写 YAML,不写 Python。

**项目地址**: <https://github.com/zxpFreesky/crm-api-autotest>

***

## 一、为什么又造一个轮子

市面上的接口测试框架很多 — pytest + requests、HttpRunner、Apifox 自动化、Postman Collection。但当我们真正落地到一个企业级 CRM 系统时,遇到的问题都很具体:

| 痛点      | 具体场景                                       |

| ------- | ------------------------------------------ |

| 测试数据写死  | 第一次跑通了,第二次因为手机号已存在、信用代码已注册而失败              |

| 接口依赖难处理 | 创建客户后要拿到 customer\_id 才能创建联系人,硬编码 ID 维护成本高 |

| 多角色权限测试 | 销售能看、助理不能看、管理员能改 — 需要同时登录多账号               |

| 失败定位靠经验 | 测试一红,开发问"为什么红",测试人员得打开日志一行行翻               |

| 用例越积越多  | 接口字段改了,几十条用例全要改                            |

| 报告推送靠截图 | CI 跑完后手动截图发群里,没人主动看 HTML 报告                |

我们的目标不是替代 HttpRunner,而是**把这些"最后一公里"问题一次性解决掉**。

***

## 二、整体架构

```

┌──────────────────────────────────────────────────────────┐

│                   AI 智能决策层                            │

│   用例生成 / 语义断言 / 失败诊断 / Self-Healing            │

│             (DeepSeek / 通义 / 智谱 / Kimi ...)           │

└──────────────────────────┬───────────────────────────────┘

                           │ MCP 协议

┌──────────────────────────┼───────────────────────────────┐

│                  MCP 服务层                               │

│   ApiPost 元数据 │ 数据库查询 │ 报告 + 企业微信通知        │

└──────────────────────────┬───────────────────────────────┘

                           │

┌──────────────────────────┼───────────────────────────────┐

│                pytest 执行层                              │

│   YamlCaseRunner │ SessionContext │ DataFactory          │

│   LoginClientFactory (多账号会话隔离)                     │

└──────────────────────────┬───────────────────────────────┘

                           │

┌──────────────────────────┼───────────────────────────────┐

│                持续反馈层                                  │

│   HTML 报告 │ AI 诊断报告 │ 企业微信 (markdown/card)       │

└──────────────────────────────────────────────────────────┘

```

核心设计原则:**测试人员只维护 YAML,复杂逻辑交给框架**。

### 项目目录分层

```

auto_api_rmp_new/

├── conftest.py                 # pytest 全局 fixture(登录工厂/会话上下文/AI/DB 统一注入)

├── pytest.ini                  # pytest 配置

├── run.py                      # 运行入口(--all/--file/--list-cases/--list-accounts)

├── test_cases/                 # 📁 测试执行层:pytest 测试文件

│   ├── test_login.py           #   登录接口测试(YAML 数据驱动)

│   ├── test_clue.py            #   线索接口测试(YAML 数据驱动)

│   └── test_ai_add_clue.py     #   Python 编程模式示例

├── datas/                      # 📁 测试数据层

│   └── yaml_cases/             #   YAML 数据驱动用例(推荐方式)

│       ├── login_cases.yaml

│       └── clue_cases.yaml

├── common/                     # 📁 公共能力层

│   ├── my_request.py           #   HTTP 请求封装(requests.Session)

│   ├── session_context.py      #   会话上下文 + 响应提取器(依赖链核心)

│   ├── data_factory.py         #   动态数据工厂(@xxx 标签 / ${xxx} 引用)

│   ├── yaml_runner.py          #   YAML 用例执行引擎(断言/提取/解析)

│   ├── get_config.py           #   配置读取(多账号角色支持)

│   ├── do_excel.py             #   Excel 读写(兼容历史用例)

│   ├── Log_packing.py          #   日志模块(按日滚动)

│   └── contants.py             #   项目路径常量

├── ai/                         # 📁 AI 能力层

│   ├── config.py               #   AI 配置管理(YAML,多模型切换)

│   ├── llm_client.py           #   统一 LLM 客户端(8 家国产大模型)

│   ├── case_generator.py       #   AI 用例智能生成(YAML + Python 双模式)

│   ├── smart_assert.py         #   语义断言引擎

│   ├── self_healing.py         #   Self-Healing 自愈引擎

│   ├── root_cause.py           #   失败根因分析引擎

│   └── prompts/                #   Prompt 模板

│       ├── case_gen.py         #     用例生成(四维度测试设计)

│       ├── assert_gen.py       #     断言生成

│       └── analysis.py         #     分析类(诊断/根因/自愈)

├── mcp_servers/                # 📁 MCP 服务层

│   ├── apipost_server.py       #   ApiPost MCP(接口元数据/调试/变更检测)

│   ├── database_server.py      #   数据库 MCP(SQL 查询/表结构/数据比对)

│   └── report_server.py        #   报告 MCP(结果收集/历史趋势/企业微信通知)

├── config/                     # 📁 配置层(敏感数据,不入 Git)

│   ├── test.ini                #   环境 URL / 日志 / 通知 / AI 开关

│   ├── users.yaml              #   多账号角色配置(销售/助理/管理员/市场)

│   ├── ai_config.yaml          #   AI 模型配置(多 provider)

│   └── *.example               #   配置模板(不含敏感数据,入 Git)

├── logs/                       # 📁 运行日志(自动生成,按日滚动)

└── reports/                    # 📁 测试报告(自动生成 HTML + JSON)

```

**分层职责对照:**

| 目录 | 层级 | 职责 | 谁来维护 |

|------|------|------|----------|

| `test_cases/` | 测试执行层 | 调用框架、组织测试 | 测试工程师(少量) |

| `datas/yaml_cases/` | 测试数据层 | 定义用例参数和断言 | 测试工程师(主要) |

| `common/` | 公共能力层 | 通用能力封装 | 框架维护者 |

| `ai/` | AI 能力层 | LLM 相关能力 | 框架维护者 |

| `mcp_servers/` | MCP 服务层 | 对外暴露的标准化能力 | 框架维护者 |

| `config/` | 配置层 | 环境/账号/模型配置 | 运维/测试工程师 |

**核心设计原则**:**测试人员只维护 YAML,复杂逻辑交给框架**。

---

## 三、四个关键设计

### 1. 动态数据工厂:消灭写死的数据

测试数据写死是接口测试最大的坑。我们设计了 `DataFactory`,YAML 中用 `@xxx` 标签声明,运行时动态生成:

```yaml

add_clue:

  data:

    customer_name: "@company"              # 随机企业名

    business_license_code: "@license"      # 随机 18 位信用代码

    phone: "@phone"                        # 随机手机号

    province: "@province"                  # 联动省市区

    city: "@city"

    town: "@town"

    remark: "@remark"                      # 带时间戳备注

```

每次运行生成不同数据,**永远不会因为数据已存在而失败**。`@license` 不是随机字符串,而是符合 GB 32100-2015 校验位规则的真实格式信用代码。

### 2. 会话上下文:业务依赖链的解法

CRM 系统典型的依赖链:线索 → 客户 → 联系人 → 商机。下游接口需要上游返回的 ID。

我们用 `SessionContext` + `DataExtractor` 解决:

```yaml

# 第 1 步:创建线索,自动提取 clue_id 到上下文

add_clue:

  method: post

  data: { customer_name: "@company", ... }

  extract:

    clue_id: data.id                       # ← 从响应提取

  assert:

    - { path: code, operator: in, expect: [200, 0] }

# 第 2 步:用 ${clue_id} 引用,无需硬编码

query_clue_detail:

  method: get

  params:

    clue_id: "${clue_id}"                  # ← 自动替换

  assert:

    - { path: data.id, operator: eq, expect: "${clue_id}" }

```

`${clue_id}` 在请求发出前会被替换为上下文中的真实值,跨用例、跨账号都能用。

### 3. 多账号会话隔离

权限测试需要同时持有多个登录态。我们用 `LoginClientFactory` 解决:

```yaml

# 销售账号创建数据

add_by_sale:

  account: sale_account_1                  # 销售登录态

  data: { ... }

  extract: { entity_id: data.id }

# 助理账号查询 — 应该看不到

query_by_assistant_invisible:

  account: assistant_account               # 助理登录态

  params:

    id: "${entity_id}"

  assert:

    - { path: code, operator: not_in, expect: [200, 0] }

```

底层每个 account key 对应一个独立的 `requests.Session` + `SessionContext`,token 自动注入、自动缓存。同账号多次调用复用 session,**100 条用例只登录一次**。

### 4. 失败诊断:从"看日志"到"看建议"

传统测试失败只告诉你"assert code in \[200, 0] failed, actual=400"。开发拿这个信息还得自己分析为什么 400。

我们的诊断引擎做了三件事:

```

失败发生

   ↓

规则分析(无 LLM 也能跑)

   - 分类:断言失败(正向)/ 断言失败(逆向通过)/ 网络错误 / 接口变更 / 服务端错误

   - 提取:期望值 vs 实际值

   - 建议:基于分类给出可操作建议

   ↓

AI 深度分析(可选,有 LLM 时启用)

   - 结合请求体 + 响应体 + 历史结果

   - 给出根因猜测

   ↓

历史对比

   - 上次通过 → 本次失败 = 回归

   - 连续 N 次失败 = 慢性 Bug

```

举个真实例子:

```

[诊断] add_clue_empty_license: 分类=断言失败(逆向通过), 风险=medium

[诊断]   断言: code 期望不在列表中 [200], 实际=200

[诊断]   建议: 逆向用例期望失败但实际通过了,说明服务端未对该场景做校验,

              需确认是否为预期行为

```

这条建议直接告诉开发:"**信用代码为空居然能创建成功,可能漏了校验**"。一句话胜过一屏日志。

***

## 四、四维度测试设计:把"经验"变成"清单"

新测试工程师写用例往往只写正向,老测试工程师能想到边界、异常、场景但难复制。我们把测试经验固化成 4 个维度,**每次生成都强制覆盖**:

### 维度一:字段边界值

| 字段类型 | 必测项                             |

| ---- | ------------------------------- |

| 字符串  | 最小长度、最大长度、空串、超长、特殊字符(SQL/emoji) |

| 数值   | 最小值、最大值、0、负数、超上限、非数字            |

| 枚举   | 每个合法值、非法值、空值、大小写混用              |

| 手机号  | 11 位、少位、多位、全 0、境外格式             |

| 信用代码 | 18 位、少位、已存在(唯一性冲突)              |

### 维度二:必填校验

**每个必填字段至少一条缺失用例**,不合并验证:

```yaml

add_clue_missing_customer_name:

  data: { ... }     # 不传 customer_name

add_clue_missing_phone:

  data: { ... }     # 不传 phone

```

为什么不合并?因为合并验证时如果失败了,**你不知道是哪个字段导致的**。

### 维度三:业务依赖链

CRUD 完整 6 步:创建 → 查询 → 编辑 → 验证修改 → 删除 → 验证已删除。每一步都依赖上一步的 ID。

### 维度四:场景测试

- 权限场景:销售 A 看不到销售 B 的数据

- 重复场景:同一信用代码二次创建应报错

- 状态场景:已删除的记录不能再删

- 列表筛选:状态 + 日期 + 关键字组合

***

## 五、AI 是怎么接入的

不夸张地说,AI 是这个框架的"放大器",但不是"主角"。我们的设计原则:

> **AI 失败时框架必须能跑,AI 成功时框架跑得更好。**

### 用例生成:四维度 prompt

```python

prompt = f"""

基于以下接口元数据,生成 YAML 测试用例:

接口: {api_metadata['name']}

路径: {api_metadata['method']} {api_metadata['path']}

字段: {api_metadata['request']['params']}

必须覆盖四个维度:

1. 正向用例 + CRUD 业务依赖链(创建→查询→编辑→列表→删除→验证删除)

2. 必填校验:每个必填字段单独生成缺失用例

3. 字段边界值:字符串长度、数值范围、枚举非法、手机号格式

4. 场景测试:权限隔离、重复创建、列表筛选组合

输出格式:YAML,每条用例包含 name/api/method/data/extract/assert

"""

```

LLM 输出 YAML 后,自动生成对应的 Python 运行器(无需 LLM),保存为两个文件:

```

datas/yaml_cases/customer_cases.yaml    # LLM 生成的用例数据

test_cases/test_customer.py             # 自动生成的运行器

```

### 失败诊断:规则 + AI 双层

第一层规则分析始终执行(不依赖 LLM),输出结构化分类和建议。第二层 AI 分析在 LLM 可用时启用,针对 `assertion_failure`、`interface_changed`、`server_error` 三类高风险场景调用 LLM 深度分析。

**关键:LLM 不可用时框架照常运行,只是建议稍微粗糙一点。**

### 国产大模型适配

8 家国产大模型通过 OpenAI 兼容协议统一接入,一行配置切换:

```yaml

llm:

  provider: zhipu        # deepseek / qwen / moonshot / doubao / hunyuan ...

  model: glm-5

  api_key: "sk-xxx"

```

为什么不用 LangChain?因为我们只用 chat completion 一个能力,**直接调 OpenAI SDK 更轻量**,没有额外依赖。

***

## 六、企业微信通知:从"被动看报告"到"主动推送"

CI 跑完没人主动看 HTML 报告,但企业微信消息一定会看。我们做了三种消息类型:

| 类型              | 适用场景 | 特点              |

| --------------- | ---- | --------------- |

| `markdown`      | 日常使用 | 支持颜色、加粗、@userid |

| `text`          | 简单通知 | 支持手机号@(仅此类型支持)  |

| `template_card` | 正式报告 | 卡片样式最专业,但无法@    |

推送时机:**测试结束 + 失败诊断完成后**自动推送,包含:

- 总用例数 / 通过数 / 失败数 / 跳过数

- 通过率 / 耗时

- 失败用例明细(最多 10 条)

- AI 诊断高/中风险项

***

## 七、配置分离:避免"一改全改"

| 文件               | 职责                 | 是否提交 Git     |

| ---------------- | ------------------ | ------------ |

| `test.ini`       | 环境 URL、日志、通知、AI 开关 | ❌ 不提交(含真实地址) |

| `users.yaml`     | 测试账号密码             | ❌ 不提交(含密码)   |

| `ai_config.yaml` | LLM API Key        | ❌ 不提交(含 Key) |

| `*.example`      | 配置模板               | ✅ 提交         |

环境切换一行搞定:

```ini

[env]

active=test      # 改成 gray / prod 立即切换

```

***

## 八、踩过的坑

### 坑 1:pytest 没统计跳过的用例

`pytest-testreport` 的 template=3 模板没有"跳过"筛选按钮。我们改了插件模板源码加上 skipped 按钮,但更重要的根因是 `_load_yaml_cases()` 在加载时就过滤了 `skip: true` 的用例,**导致 pytest 根本没收集到这些用例**。修复后让 pytest 收集全部用例,运行时再 `pytest.skip()`。

### 坑 2:dict.get 的立即求值

```python

# 看起来对,实际错了

user_data = self._users_config.get("default", self.get_user())

```

`dict.get(key, default)` 的 default 是**立即求值**的。即使 `default` 键存在,`self.get_user()` 也会被调用,导致去读已被注释的配置段。改成显式判断:

```python

if "default" in self._users_config:

    return self._users_config["default"]

return self.get_user()

```

### 坑 3:根因分析输出"未分类错误: self=\<object at 0x...>"

初版根因分析直接把整个 traceback 塞进错误信息,输出全是 Python 内部对象地址。重写后优先从断言语义分类(区分正向/逆向失败),提取期望值和实际值,**输出能直接给开发看的人话**。

***

## 九、效果数据

实际跑了几个 CRM 模块(客户、线索、联系人、商机):

| 指标       | 数据                 |

| -------- | ------------------ |

| 单模块用例数   | 30\~50 条(含四维度)     |

| 用例编写时间   | 单接口 5\~10 分钟(YAML) |

| 重复运行稳定性  | 99%+(动态数据保证)       |

| 失败诊断准确率  | 约 80%(规则 + AI)     |

| 企业微信通知延迟 | < 5 秒              |

***

## 十、后续规划

1. **代码 diff 风险扫描**:开发提 MR 时自动扫描代码变更,提示影响哪些接口、建议重点测什么

2. **缺陷模式积累**:把每次失败 + 修复方案存进向量库,下次同类失败时召回

3. **Hermes Agent 接入**:通过 MCP 协议接入 Hermes,实现自然语言驱动测试("跑一下客户模块的回归")

4. **飞书/钉钉通知**:当前只支持企业微信,扩展到更多平台

5. **CI/CD 集成模板**:提供 GitHub Actions / Jenkins / GitLab CI 的现成配置

***

## 十一、总结

这个框架不是"AI 替代测试工程师",而是"**AI 把测试工程师从重复劳动中解放出来**"。

测试工程师的核心能力 — 业务理解、场景设计、风险评估 — 不会被 AI 替代。但 YAML 用例的生成、失败原因的初步定位、报告的推送分发,这些重复性工作完全可以交给框架。

我们追求的是:**让测试工程师把 80% 的时间花在设计上,20% 的时间花在执行上**。而不是反过来。

***

> **项目地址**: <https://github.com/zxpFreesky/crm-api-autotest>

> **技术栈**: Python 3.8+ / pytest / requests / YAML / 智谱 GLM / 企业微信机器人

> **License**: MIT


 

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐