前言
这是从 0 开始学 AI 测试的第二篇,后续篇章会在星球里更新, 社区这里可能后续只有节选了。 感兴趣的同学可以加入星球学习。 下面我们开始讲解 AI 测试入门最重要的一课,如何让 AI 理解你的业务并生成自动化测试代码。
AI 生成代码的原理:知识从哪里来
大模型并不"认识"你的系统
很多人第一次使用 Cursor 生成接口测试时会困惑, 大模型怎么知道我的系统的接口张什么样子?如果我们强行让它写,它绝对会写出一个假的的测试用例。这是因为大模型没有你系统的任何知识。它只能根据通用经验"猜"出一个看起来合理的接口,但猜的结果和你真实的系统大概率对不上。
AI 要生成准确的测试代码,前提是你必须先把接口知识喂给它。
知识的最佳来源:API 文档
如果你的团队有完善的 API 文档(Swagger、Postman Collection、API Blueprint 等),直接把相关接口文档丢给 AI 就可以了。
在 Cursor 中,你就可以:
(Agent 模式)
@docs/api/order-api.md ← 引用文档文件
根据以上接口文档,帮我编写创建订单接口的完整测试用例,
包含正常创建、缺少必填字段、金额为负数三个场景。
AI 拿到文档后,可以准确理解接口的路径、方法、入参、返回值,生成的代码会非常准确。
没有文档怎么办:用 F12 抓包
现实情况是,很多团队的 API 文档严重滞后于代码,甚至根本没有文档。这时候最直接的办法是用浏览器开发者工具(F12)抓包。
PS: 我就是这么干的。
操作步骤
第一步:打开浏览器,进入你要测试的功能页面,按 F12 打开开发者工具,切换到 Network 标签页。
第二步:执行你要测试的操作(比如点击"创建订单"按钮),观察 Network 面板捕获到的请求。
第三步:找到目标请求,右键 → Copy → Copy as cURL,或者手动记录以下信息:
接口路径: POST /api/v2/orders
请求头: Content-Type: application/json
Authorization: Bearer <token>
请求体: {
"product_id": "prod_001",
"quantity": 2,
"address_id": "addr_123",
"coupon_code": "SAVE10",
"remark": "请尽快发货"
}
响应状态码: 201
响应体: {
"code": 0,
"message": "success",
"data": {
"order_id": "ord_20260613_001",
"status": "pending",
"total_amount": 198.00,
"created_at": "2026-06-13T14:00:00Z"
}
}
第四步:把抓到的信息 + 对功能的简单描述一起给 AI:
(Agent 模式)
我抓到了一个创建订单的接口,信息如下:
接口:POST /api/v2/orders
请求体:
{
"product_id": "prod_001",
"quantity": 2,
"address_id": "addr_123",
"coupon_code": "SAVE10",
"remark": "请尽快发货"
}
响应(成功):
{
"code": 0,
"data": {
"order_id": "ord_20260613_001",
"status": "pending",
"total_amount": 198.00
}
}
功能说明:用户选好商品后,提交订单。product_id 和 quantity 是必填的,
address_id 是收货地址,coupon_code 是优惠券(可选),remark 是备注(可选)。
请根据以上信息,帮我编写完整的接口测试用例。
AI 会"推理"你没说清楚的部分
你会发现,上面的描述并没有告诉 AI 每个字段的所有细节(比如 quantity 的取值范围、product_id 的格式约束)。但这没关系——大模型会根据你描述的业务功能,自主推理出合理的测试边界。
比如 AI 会自动推断:
-
quantity可能有最小值(不能为 0 或负数) -
coupon_code是可选字段,需要测试不传的情况 -
product_id不存在时应该报错 - 库存不足时应该有对应的错误响应
AI 生成的测试用例可能包含你没想到的边界场景,这正是它的价值所在。
当 AI 推理错了怎么办?
直接纠正它:
你刚才假设 quantity 最大值是 99,实际上我们的系统限制是 999,
而且当超过库存数量时才会报错,不是超过 999 就报错。请修正。
AI 会立刻根据你的纠正更新测试用例,并自动调整相关的边界测试场景。这个过程类似于"代码审查"——AI 写草稿,你来校正,效率远高于从零手写。 当然也可以从一开始就把完整的测试用例给 AI, 这样就不用 AI 自己瞎猜了, 事实上我们项目中基本都是这样的。 但如果我们刚刚开始接受项目, 还没有完善的测试用例的时候, 也可以让 AI 自己去探索, 然后我们来纠正的这样一个模式。
那么接下来问题就来了, 以后我们每写一个自动化测试用例,都需要这么麻烦的去抓每个接口的包么? 事实上我们之前抓过的接口是不用重新给大模型的,这就涉及到 AI 编程最重要的一个部分了, 就是语义检索。
语义检索:存量代码就是最好的参考
Cursor 的代码库索引机制
当你在 Cursor 中打开一个项目,Cursor 会在后台自动对整个代码库做 Embedding(向量化),并存入向量数据库。
这意味着:
- 你写的每一行代码,Cursor 都"读"过了
- 当你提出新的需求时,AI 会通过语义检索找到项目中最相关的代码作为参考
- 它不是简单的关键词匹配,而是理解代码的语义含义
你可以在 Cursor 右下角看到索引进度:Indexing codebase... (234/312 files),索引完成后所有文件都可以被语义检索到。
PS:版本不同,这段信息的位置可能会有所不同。
存量代码如何成为"活文档"
让我们看一个具体的例子。
假设你按照第之前教程的方法,已经写好了第一个测试用例:
# tests/order/test_create_order.py
class TestCreateOrder:
def test_create_order_success(self, api_client):
response = api_client.post("/api/v2/orders", json={
"product_id": "prod_001",
"quantity": 2,
"address_id": "addr_123"
})
assert response.status_code == 201
data = response.json()["data"]
assert "order_id" in data
assert data["status"] == "pending"
现在你想写"查询订单详情"的测试用例,你只需要告诉 AI:
帮我编写一个查询订单的测试用例。 它的测试步骤:
1. 创建订单,获取订单ID
2. 查询订单, 检验XXXXXX。 PS: 如果查询订单之前没有写过相关用例, 这里也需要去抓查询订单的接口的包。
此时 AI 不需要你重新解释创建订单的接口,大模型通过语义检索已经找到了 test_create_order.py,直接参考现有风格生成新用例:
# AI 自动生成,风格与现有代码完全一致
class TestGetOrder:
def test_get_order_success(self, api_client):
# 先创建一个订单,再查询它
create_resp = api_client.post("/api/v2/orders", json={
"product_id": "prod_001",
"quantity": 2,
"address_id": "addr_123"
})
order_id = create_resp.json()["data"]["order_id"]
response = api_client.get(f"/api/v2/orders/{order_id}")
assert response.status_code == 200
data = response.json()["data"]
assert data["order_id"] == order_id
assert data["status"] == "pending"
assert "items" in data
关键点:甚至你可以只说"查询订单详情",AI 自己知道要先创建订单再查询(因为它从已有代码中理解了这个业务流程)。
当然这样的形式实在过于代码冗余了,我们需要更灵活的接口,以及让我们的知识更加的简单。 毕竟以后代码库里的 case 越来越多, 大模型的语义检索能力也会收到干扰,并且我们的测试用例也得分层,不能把所有东西都写在用例里, 需要把 API,业务逻辑等等单独分层。 那怎么办呢? 看下面的内容。
增量知识积累
这就是语义检索带来的核心价值:项目里的代码越多,AI 能参考的知识越多,生成的代码质量越高。
第1个接口测试 → AI 需要你提供完整接口信息
第5个接口测试 → AI 已经理解了你的项目结构和风格
第20个接口测试 → AI 直接参考存量代码,你只需说"仿照 XXX 写一个 YYY"
第50个接口测试 → AI 几乎不需要额外解释,自主完成
实践建议:
- 每次引入一个新模块时,先手动写(或精心指导 AI 写)一个"示范性"测试用例
- 这个示范用例会成为该模块所有后续测试的参考基准
- 后续只需要增量录入新的业务知识(新字段、新规则),存量的调用方式 AI 自己会找
当代码多起来:Service 层抽象
语义检索也会"迷路"
随着项目发展,测试代码会越来越多。假设你已经有了 100 个测试文件,每个文件里都有调用各种接口的代码。这时候会出现两个问题:
问题一:同一功能有多种实现方式
# 测试文件 A 里创建订单的方式
api_client.post("/api/v2/orders", json={"product_id": "p1", "quantity": 1, ...})
# 测试文件 B 里(早期写的,字段名不一样)
api_client.post("/api/v2/orders", json={"productId": "p1", "qty": 1, ...})
# 测试文件 C 里(用了另一套封装)
order_service.create(product="p1", count=1)
AI 做语义检索时,可能找到旧的、不规范的写法,导致生成的代码风格不一致,甚至用了已经废弃的字段名。
问题二:检索噪音增大
100 个文件里有大量相似的接口调用代码,AI 在检索时需要处理大量干扰项,可能反而找不到最相关的参考,生成质量下降。
解决方案:API 层 + Service 层
工程化的解法是将代码分成三层:
tests/ ← 测试用例层(只关心测试逻辑)
└── order/
└── test_create_order.py
services/ ← 业务逻辑层(封装多个接口的串联调用)
└── order_service.py
api/ ← 接口调用层(每个接口对应一个函数,一处定义)
└── order_api.py
api/order_api.py:接口调用层
每个接口只在这里定义一次,是整个工程的"接口字典":
# api/order_api.py
"""
订单模块接口定义
所有订单相关接口的调用都从这里发出,禁止在测试文件中直接拼接接口路径。
"""
from utils.http_client import APIClient
def create_order(client: APIClient, product_id: str, quantity: int,
address_id: str, coupon_code: str = None, remark: str = None):
"""创建订单"""
payload = {
"product_id": product_id,
"quantity": quantity,
"address_id": address_id,
}
if coupon_code:
payload["coupon_code"] = coupon_code
if remark:
payload["remark"] = remark
return client.post("/api/v2/orders", json=payload)
def get_order(client: APIClient, order_id: str):
"""查询订单详情"""
return client.get(f"/api/v2/orders/{order_id}")
def list_orders(client: APIClient, page: int = 1, page_size: int = 20, status: str = None):
"""查询订单列表"""
params = {"page": page, "page_size": page_size}
if status:
params["status"] = status
return client.get("/api/v2/orders", params=params)
def cancel_order(client: APIClient, order_id: str, reason: str):
"""取消订单"""
return client.post(f"/api/v2/orders/{order_id}/cancel", json={"reason": reason})
services/order_service.py:业务逻辑层
把需要多个接口串联的常见业务流程封装成公共方法:
# services/order_service.py
"""
订单业务逻辑层
封装常用的多步骤业务流程,供测试用例直接调用。
"""
from api.order_api import create_order, get_order, cancel_order
from utils.http_client import APIClient
def create_and_get_order(client: APIClient, product_id: str, quantity: int,
address_id: str) -> dict:
"""
创建订单并立即查询,返回完整订单信息。
适用于需要验证创建后状态的测试场景。
"""
create_resp = create_order(client, product_id, quantity, address_id)
assert create_resp.status_code == 201, f"创建订单失败: {create_resp.text}"
order_id = create_resp.json()["data"]["order_id"]
get_resp = get_order(client, order_id)
assert get_resp.status_code == 200
return get_resp.json()["data"]
def create_pending_order(client: APIClient) -> str:
"""
使用默认参数快速创建一个待支付订单,返回 order_id。
适用于其他测试(如支付测试、取消测试)的前置条件。
"""
resp = create_order(
client,
product_id="prod_test_001",
quantity=1,
address_id="addr_test_001"
)
assert resp.status_code == 201
return resp.json()["data"]["order_id"]
测试文件如何使用这两层
# tests/order/test_create_order.py
from api.order_api import create_order
from services.order_service import create_and_get_order
class TestCreateOrder:
def test_create_order_success(self, api_client):
"""正常创建订单"""
order = create_and_get_order(api_client, "prod_001", 2, "addr_123")
assert order["status"] == "pending"
assert order["total_amount"] > 0
def test_create_order_missing_product_id(self, api_client):
"""缺少必填字段 product_id"""
resp = create_order(api_client, product_id="", quantity=1, address_id="addr_123")
assert resp.status_code == 400
assert resp.json()["message"] == "product_id 不能为空"
分层架构对 AI 的好处
有了 API 层 + Service 层之后,AI 做语义检索的效果会大幅提升:
| 没有分层 | 有分层 |
|---|---|
| AI 在 100 个测试文件里搜索"如何调用创建订单接口" | AI 直接检索到 api/order_api.py,一处即知 |
| 找到多种写法,不知道用哪个 | 接口层唯一,写法统一 |
| 多步骤流程散落在各个测试文件里 | Service 层集中封装,直接复用 |
| 新测试参考旧测试,风格越来越乱 | 新测试参考 API 层和 Service 层,风格一致 |
升级后的工程目录结构:
api-autotest/
├── api/ ← 接口调用层(每个接口定义一次)
│ ├── order_api.py
│ ├── user_api.py
│ └── product_api.py
├── services/ ← 业务逻辑层(多接口串联的公共流程)
│ ├── order_service.py
│ └── user_service.py
├── tests/ ← 测试用例层(只关心测试逻辑)
│ ├── order/
│ ├── user/
│ └── product/
├── utils/
├── fixtures/
├── config/
├── conftest.py
└── pytest.ini
Rules 文件:让 AI 遵守你的工程规范
为什么需要 Rules 文件
有了分层架构之后,还有一个问题:AI 不知道你定了这些规范。
下次你让 AI 写测试用例时,它可能还是会在测试文件里直接写 api_client.post("/api/v2/orders", ...),而不是调用你封装好的 create_order() 函数。
解决方法是使用 Cursor Rules 文件——这是 Cursor 的一个核心功能,让你可以把工程规范直接"写给 AI 看",让它在每次生成代码时都遵守这些规则。
Rules 文件是什么
Rules 文件本质上是附加在每次 AI 对话中的系统提示词(System Prompt)。你在 Rules 文件里写的内容,会在每次 AI 生成代码前被自动注入,让 AI 理解你的项目规范。
Cursor 支持两种 Rules:
| 类型 | 存放位置 | 作用范围 |
|---|---|---|
| Project Rules | .cursor/rules/*.mdc |
只对当前项目生效 |
| User Rules | Cursor Settings → Rules | 对所有项目全局生效 |
推荐方案:工程规范放 Project Rules,个人偏好(比如"回复用中文")放 User Rules。
如何创建 Rules 文件
方法一:通过 Cursor 界面创建(推荐新手)
- 打开 Cursor,进入你的项目
- 按
Cmd+Shift+P(macOS)/Ctrl+Shift+P(Windows)打开命令面板 - 输入
New Cursor Rule,回车 - 输入规则名称(比如
api-test-rules),回车 - Cursor 会自动在
.cursor/rules/目录下创建api-test-rules.mdc文件
方法二:手动创建
mkdir -p .cursor/rules
touch .cursor/rules/api-test-rules.mdc
然后用 Cursor 打开这个文件,开始编写规则内容。
4.4 Rules 文件的格式
.mdc 文件支持 Markdown 格式,开头有一段 YAML Front Matter 用于配置规则的触发时机:
---
description: 接口自动化工程编码规范
globs: # 匹配哪些文件时触发此规则
- "tests/**/*.py"
- "api/**/*.py"
- "services/**/*.py"
alwaysApply: false # 是否对所有文件始终生效
---
globs 的作用:当 AI 正在操作匹配这些路径的文件时,自动附加此规则。比如你正在写测试文件,Rules 会自动生效,不需要你手动提醒 AI。
alwaysApply: true:如果你希望所有操作都遵守这条规则(比如"始终用中文回复"),可以设置为 true。
4.5 为接口测试工程编写 Rules 文件
下面是一个完整的示例,可以直接用于你的接口测试工程:
---
description: 接口自动化工程编码规范
globs:
- "tests/**/*.py"
- "api/**/*.py"
- "services/**/*.py"
alwaysApply: false
---
# 接口自动化工程编码规范
## 工程结构
本工程采用三层架构,职责如下:
- `api/`:接口调用层,每个接口有且只有一处定义,命名规范为 `{模块名}_api.py`
- `services/`:业务逻辑层,封装需要多接口串联的公共业务流程
- `tests/`:测试用例层,只关注测试逻辑,不直接拼接接口路径
## 编写测试用例的强制规范
### 1. 禁止在测试文件中直接调用 HTTP 客户端
**错误示范**(禁止):
def test_create_order(api_client):
api_client.post("/api/v2/orders", json={...}) # ❌ 禁止
**正确示范**:
from api.order_api import create_order
from services.order_service import create_pending_order
def test_create_order(api_client):
resp = create_order(api_client, ...) # ✅ 使用 api 层
### 2. 优先使用 Service 层的公共方法
当一个操作需要多步接口调用时,优先检查 `services/` 目录下是否已有封装好的方法。
例如需要"创建订单后查询"时,使用 `order_service.create_and_get_order()`,
而不是手动写两次接口调用。
### 3. 新增接口时必须先在 api 层定义
如果需要调用一个 `api/` 目录下还没有的接口,必须先在对应的 `{模块}_api.py` 文件中定义该接口函数,然后再在测试文件中引用。
### 4. 测试用例命名规范
- 文件名:`test_{被测功能}.py`(如 `test_create_order.py`)
- 类名:`Test{功能名驼峰}` (如 `TestCreateOrder`)
- 方法名:`test_{场景描述}`(如 `test_create_order_missing_product_id`)
### 5. 断言规范
- 必须断言 HTTP 状态码
- 必须断言响应体中的核心业务字段
- 错误场景必须断言错误信息的具体内容(`message` 字段)
## 语言规范
- 所有代码注释使用中文
- docstring 必须说明:函数功能、适用测试场景
4.6 Rules 文件生效后的效果
有了这个 Rules 文件,当你告诉 AI:
帮我写一个支付订单的测试用例,接口是 POST /api/v2/orders/{order_id}/pay,
需要先有一个待支付的订单
AI 会自动:
-
检查
api/order_api.py是否已有pay_order()函数,没有的话先补充 -
检查
services/order_service.py是否有create_pending_order()可复用,有的话直接用 - 生成符合命名规范的测试类和方法名
- 断言状态码 + 业务字段
# AI 按照 Rules 规范自动生成:
# 1. 先补充 api 层(如果没有)
# api/order_api.py
def pay_order(client: APIClient, order_id: str, payment_method: str):
"""支付订单"""
return client.post(f"/api/v2/orders/{order_id}/pay",
json={"payment_method": payment_method})
# 2. 测试用例(复用了 Service 层的 create_pending_order)
# tests/order/test_pay_order.py
from api.order_api import pay_order
from services.order_service import create_pending_order
class TestPayOrder:
def test_pay_order_success(self, api_client):
"""正常支付:使用微信支付成功场景"""
order_id = create_pending_order(api_client) # 复用 Service 层
resp = pay_order(api_client, order_id, "wechat")
assert resp.status_code == 200
assert resp.json()["data"]["status"] == "paid"
def test_pay_order_already_paid(self, api_client):
"""异常:重复支付已完成的订单"""
order_id = create_pending_order(api_client)
pay_order(api_client, order_id, "wechat") # 第一次支付
resp = pay_order(api_client, order_id, "wechat") # 第二次支付
assert resp.status_code == 409
assert resp.json()["message"] == "订单已支付"
完全符合你定义的规范,不需要你再手动检查和纠正。
4.7 Rules 文件的进阶技巧
技巧一:为不同目录创建不同 Rules
.cursor/rules/ 目录下可以有多个 .mdc 文件,每个针对不同场景:
.cursor/rules/
├── api-test-rules.mdc # 接口测试规范(作用于 tests/ api/ services/)
├── api-layer-rules.mdc # API 层开发规范(作用于 api/)
└── general-rules.mdc # 通用规范(alwaysApply: true)
技巧二:在 Rules 中引用文件
可以在 Rules 中指定"参考这个文件的风格":
## 代码风格参考
当生成新的接口函数时,参考 `api/order_api.py` 的代码风格。
当生成新的测试类时,参考 `tests/order/test_create_order.py` 的结构。
技巧三:把常见错误场景也写进 Rules
## 常见错误与修正
- **不要使用 time.sleep()**,如需等待,使用 pytest 的 `poll` 机制或重试装饰器
- **不要硬编码 token**,必须从环境变量读取
- **不要在测试中创建真实的外部资源**(如发短信、发邮件),使用 mock
5. 完整实践流程总结
把本篇所有内容串起来,形成一套完整的工作流:
┌─────────────────────────────────┐
│ Step 1:喂知识给 AI │
│ API 文档 OR F12 抓包 + 功能描述 │
└──────────────┬──────────────────┘
│
┌──────────────▼──────────────────┐
│ Step 2:写第一个测试用例 │
│ AI 生成 → 你校正 → 确认无误 │
└──────────────┬──────────────────┘
│
┌──────────────▼──────────────────┐
│ Step 3:利用语义检索扩展 │
│ 存量代码成为参考,增量知识即可 │
└──────────────┬──────────────────┘
│ 代码多了,语义噪音↑
┌──────────────▼──────────────────┐
│ Step 4:抽象 API 层 + Service 层 │
│ 接口一处定义,公共流程集中封装 │
└──────────────┬──────────────────┘
│
┌──────────────▼──────────────────┐
│ Step 5:编写 Rules 文件 │
│ 把工程规范"写给 AI 看",强制遵守 │
└──────────────┬──────────────────┘
│
┌──────────────▼──────────────────┐
│ 持续扩展:只需录入增量知识 │
│ 新接口 → 定义到 api 层 → 写测试 │
└─────────────────────────────────┘
| 阶段 | 你做什么 | AI 做什么 |
|---|---|---|
| 喂知识 | 提供接口信息或抓包数据 | 理解接口,推断边界场景 |
| 写第一个用例 | 审查并纠正 AI 的推断 | 生成完整测试代码 |
| 扩展阶段 | 只说"帮我写 XXX 的测试" | 语义检索参考存量代码,自主生成 |
| 工程治理 | 维护 api 层、service 层、rules 文件 | 严格遵守规范,风格统一 |
小结
基本上,掌握了今天讲的内容, 基本上用于生成自动化测试用例领域的 AI 已经可以满足大部分需求。 事实上,AI 生成测试用例的关键就在于知识库的设计。 而我们当前的代码仓库和 rules 文件其实都是知识的一种, 它们用来指导 AI 来完成测试用例的生成。所以我再星球里一直都强调:Everything in git 的重要性。 只要放在工程里, 大模型就可以去通过语义检索来理解它,然后生成测试用例。
这里再宣传一下我得知识星球,6 月份我也搞了一批优惠券,现在加入有 8 折优惠,先到先得:
