从Swagger到可执行测试:基于Skills+RAG的接口用例智能生成实践

0 阅读7分钟

一、Swagger生成了一堆代码,但用例还是得手写

2025年我接手一个微服务项目,Swagger文档写得整整齐齐。测试团队用Swagger Codegen生成了API客户端,然后继续手动写测试用例。

不是懒。是生成的代码只解决了“怎么调用”,没解决“用什么参数调用”和“怎么判断对错”。

一个典型场景:接口要求传userId,生成器给你生成了userId: 0。跑不通,因为0不是合法用户。测试人员得手工改成10001。接口要求传订单状态,生成器给你生成了status: "string"。也跑不通,因为枚举值只有“PAID”“UNPAID”。

到了2026年,AI生成用例的工具多起来了。但大部分还是把Swagger直接塞给大模型,出来一堆看起来像用例、实际跑不通的代码。根本原因不是模型不行,是模型看不到文档之外的东西——业务规则、数据依赖、断言经验。

二、本质是接口文档缺少“可执行的上下文”

先说清楚:Swagger(OpenAPI)是一个优秀的描述规范。它能把接口的路径、参数类型、必填属性、响应结构讲清楚。

但一个能在CI里跑通的测试用例,需要的上下文远不止这些:

  • 合法的业务数据示例(userId必须是DB里真实存在的)
  • 边界值规则(age范围1-120,超过400报错)
  • 调用链路依赖(先调登录拿token,再调业务接口)
  • 断言规则(响应里code=0时data不能为空)

这些东西,Swagger里一个都没有。测试人员写用例时,脑子里调用了两类知识:技术规范(来自Swagger)和业务经验(来自规则库、历史缺陷、领域知识)。

所以AI生成用例失败的根本原因:模型只看到了前半部分,看不到后半部分。

解决方案也不是训练一个更大的模型记住所有规则。实际可行的工程路径是:用RAG把业务规则注入,用Skills把用例生成拆解成可编排的能力单元。

三、核心机制:Skills拆解任务 + RAG注入隐性规则

我们内部跑通了一套方案。先看整体架构:

两个核心机制拆开讲。

机制一:Skills - 把“写用例”拆成可编排的原子能力

不让LLM一次生成整个测试文件。任务太复杂,模型容易出错。拆成三个独立Skill:

  • 参数构造Skill:输入参数名、类型、约束,输出一组合法的测试数据值。对于依赖外部数据的参数,自动插入获取逻辑。
  • 依赖链处理Skill:分析接口的前置条件,生成setup代码。比如需要登录态,它自动生成调用登录接口并提取token的代码块。
  • 断言生成Skill:根据响应schema和业务规则,生成状态码断言、字段存在性断言、值范围断言。

每个Skill有独立的prompt模板,调用时只关注自己的职责。Skill之间通过一个简单的编排器组合。比如生成下单接口用例,流程是:依赖链Skill先获取token → 参数构造Skill生成商品ID、数量 → 断言Skill生成响应校验。

为什么这么做:拆分后每个任务足够单纯,LLM出错的概率指数级下降。而且Skill可以跨接口、跨项目复用。换一个项目,只需要换RAG里的规则库,不用改Skill。

机制二:RAG - 把“隐性规则”变成可检索的知识

业务规则怎么喂给模型?我们维护了一个规则库,每条规则是自然语言 + 对应的代码示例:

  • “userId参数必须是系统中已注册的用户,测试环境固定可用ID为10001、10002”
  • “当productId对应的商品库存为0时,响应中的code应为400,message包含'库存不足'”
  • “分页接口的pageSize最大100,传入101时应返回参数错误”

这些规则被向量化存入向量数据库。当LLM要生成某个接口的用例时,RAG检索器根据接口路径、参数名、甚至参数描述,召回最相关的5-10条规则,拼接到prompt里。

实测效果:以前LLM生成quantity: 0,现在生成quantity: 1(因为规则说最小值是1)。以前断言只校验HTTP 200,现在会校验data.orderId不为空。生成的用例从“语法正确但跑不通”变成了“直接可执行”。

四、实战对比:一个订单接口的两种生成方式

拿一个真实接口:POST /order/create,Swagger定义如下:

  • userId: int, required
  • productId: int, required
  • quantity: int, 最小值1,最大值999
  • couponId: int, optional

响应:orderId(string), totalPrice(decimal)

方式一:直接让LLM读Swagger生成

输出用例片段(伪代码):

def test_create_order():
    resp = client.post("/order/create", json={
        "userId": 123,
        "productId": 456,
        "quantity": 0,
        "couponId": 789
    })
    assert resp.status_code == 200

问题:userId=123可能不存在,quantity=0违反约束,couponId=789可能无效。断言只检查200,不检查业务字段。这个用例跑不通,即使能跑通也没覆盖任何有价值的校验。

方式二:Skills+RAG生成

规则库里有:

  • “userId用10001,该用户已在测试环境预置”
  • “productId用20001,该商品库存充足”
  • “quantity边界值为1和999,无效值为0和1000”
  • “优惠券必须属于当前用户,否则接口返回410”

生成结果:

def test_create_order_success():
    # 依赖链Skill自动处理登录
    token = login(user_id=10001)
    client.set_token(token)
    
    resp = client.post("/order/create", json={
        "userId": 10001,
        "productId": 20001,
        "quantity": 1,
        "couponId": None
    })
    assert resp.status_code == 200
    assert resp.json()["orderId"] isnotNone
    assert resp.json()["totalPrice"] > 0

def test_create_order_quantity_boundary():
    token = login(user_id=10001)
    client.set_token(token)
    
    # 边界值:999应成功
    resp = client.post("/order/create", json={
        "userId": 10001,
        "productId": 20001,
        "quantity": 999,
        "couponId": None
    })
    assert resp.status_code == 200
    
    # 边界值:1000应失败
    resp = client.post("/order/create", json={
        "userId": 10001,
        "productId": 20001,
        "quantity": 1000,
        "couponId": None
    })
    assert resp.status_code == 400

def test_create_order_invalid_coupon():
    # 使用不属于该用户的优惠券
    ...

三个用例全部可运行。参数值来自规则库,断言覆盖了业务字段,边界条件自动生成。生成时间不到1分钟,人工写至少20分钟。

五、工程落地:最小可行方案与踩坑经验

说给测试开发和效能工程师。不需要大厂资源,用开源工具就能搭。

第一步:准备Swagger + 手工规则库

Swagger从knife4j或Springfox导出JSON。规则库先用手工整理,每个接口写3-5条规则,存成markdown或txt。不用追求全面,先跑通流程。

第二步:选RAG框架 + 本地模型

推荐LlamaIndex或LangChain。模型用Qwen2-7B或ChatGLM3-6B,Ollama本地部署。规则库用chromadb或faiss做向量存储。

第三步:实现三个Skill的prompt模板

参数构造Skill的prompt核心片段:

你是测试数据生成专家。根据以下参数定义和检索到的业务规则,生成一组测试参数值。
输出JSON数组,每个元素是一组完整的参数。不要输出解释。

依赖链Skill的prompt:

分析接口的前置依赖。如果需要登录,生成获取token的代码片段。如果需要预置数据,生成对应的setup代码。
输出Python代码,不要额外说明。

第四步:编排器写一个200行的Python脚本

读取Swagger → 遍历接口 → RAG检索 → 调用三个Skill → 组装成pytest格式 → 写入文件。

踩过两个坑:

  • 坑1:RAG召回不准。解决方案:在规则入库时增加元数据(接口路径、参数名),检索时用精确匹配+向量检索混合。
  • 坑2:Skill输出的代码有语法错误。解决方案:增加一个校验步骤,把生成的代码丢给Python的compile()做语法检查,失败则重试一次。

这套方案一周内搭完,投入产出比很高。

六、你的接口测试用例能直接运行吗

最后问一个实际问题:

你现在的接口测试用例库,如果换一台干净的机器,没有任何手工配置的数据,能直接跑通吗?

如果不能,说明你的用例和可执行之间隔着一层手工操作。这层操作,就是可以用Skills+RAG抹掉的东西。

霍格沃兹测试开发学社,是一个专注软件测试、自动化测试、人工智能测试与测试开发的技术交流社区