随着微服务架构的普及,API 端点的数量呈指数级增长,全面彻底地测试这些 API 的成本也随之水涨船高。即使存在 OpenAPI (Swagger) 规范,编写 API 测试用例仍然是一项手动、重复性的工作。那么,我们是否可以仅仅依靠 OpenAPI schema 自动生成有意义的 测试用例呢?近年来,大型语言模型 (LLMs) 的崛起为我们提供了一种新的可能性。本文将深入探讨如何结合 OpenAPI Schema、检索增强生成 (RAG) 和 LLMs,实现 API 测试用例的自动化生成,并介绍一个名为 Caseforge 的开源原型工具,来验证这一设想。

Caseforge:API 测试用例自动生成的概念验证

Caseforge 是一个实验性的工具,旨在直接从 OpenAPI schema 自动生成和执行 API 测试用例。它支持:

  • 解析和分析 OpenAPI 3.x 规范。
  • 提取端点结构和参数细节。
  • 使用 RAG 为 LLMs 提供必要的上下文信息。
  • 生成结构化的 JSON 格式的 测试套件(包括链式多步请求)。
  • 执行测试并在 Web UI 中可视化结果。

目前,该项目仍处于积极开发阶段,可能存在一些缺陷和不完善之处。然而,初步结果表明,使用 LLMs 驱动的自动化 测试用例 生成方法具有巨大的潜力。例如,在对一个包含 50 个 API 端点的电商平台进行测试时,Caseforge 成功生成了超过 200 个 测试用例,涵盖了用户认证、商品浏览、购物车管理和订单支付等多个核心业务流程。这些 测试用例不仅包含了正向测试,还包含了各种边界情况和异常情况的测试,有效提升了测试覆盖率和测试效率。

系统架构:OpenAPI Schema 到可执行测试的转换

Caseforge 的架构设计主要围绕着如何有效地利用 OpenAPI Schema 信息,通过 RAG 和 LLMs 最终生成可执行的 测试用例。其核心流程如下:

  1. Schema 解析: 首先,Caseforge 解析 OpenAPI schema,并解决其中的 $ref 引用,将 schema 信息转换成程序可以理解和处理的结构化数据。
  2. Chunking 与 Embedding: 将提取的端点规范和 schema 分割成小的文本块 (chunks),然后使用 embedding 模型(例如 OpenAI 的 text-embedding-ada-002)将这些文本块转换成向量表示,并将这些向量存储到向量数据库中。
  3. RAG + LLM Prompting: 当需要生成 测试用例 时,Caseforge 使用 RAG 技术,根据目标 API 端点,从向量数据库中检索最相关的 schema chunks。然后,将检索到的 schema 信息和精心设计的 prompt 一起传递给 LLM。
  4. LLM 输出解析: LLM 根据 prompt 生成 JSON 格式的 测试套件。Caseforge 对 LLM 的输出进行验证,确保其符合预定义的 schema,并将验证后的 测试套件 存储起来。
  5. 执行: Caseforge 解释 测试套件,并逐步执行其中的每个测试步骤,包括变量提取和跨步骤的链式调用。

Caseforge 的主要组成部分包括:

  • FastAPI 后端: 用于 schema 解析、LLM 编排和测试执行。
  • Next.js 前端: 用于管理 schema、查看 测试用例 和触发测试运行。
  • 向量存储: 使用 PostgreSQL + pgvector 存储和检索 OpenAPI 上下文信息。
  • LLM 支持: 支持本地 LLMs(例如 LM Studio)或 OpenAI 兼容的 API。

举例来说,假设我们有一个用于管理用户的 API,其 OpenAPI schema 描述了创建用户 (POST /users)、获取用户 (GET /users/{id}) 和更新用户 (PUT /users/{id}) 等端点。当需要为更新用户 (PUT /users/{id}) 端点生成 测试用例 时,Caseforge 会首先从向量数据库中检索与该端点相关的 schema 信息,例如请求参数、响应格式和错误码等。然后,将这些信息传递给 LLM,并要求 LLM 生成包含正向和负向测试的 测试用例

Prompt 工程:指导 LLM 生成高质量测试用例

Prompt 工程是 Caseforge 项目的核心。精心设计的 prompt 可以有效地指导 LLM 生成高质量的 测试用例。在 Caseforge 中,prompt 的设计主要考虑以下几个方面:

  • 提供相关上下文: Prompt 需要包含目标端点、输入 schema 和依赖项等相关上下文信息,以便 LLM 能够理解 API 的功能和使用方式。
  • 要求生成正向和负向测试: Prompt 需要明确要求 LLM 生成正向测试(验证 API 在正常情况下的行为)和负向测试(验证 API 在异常情况下的行为)。例如,对于更新用户 API,正向测试可以验证使用有效数据更新用户信息是否成功,负向测试可以验证当缺少必填字段或使用无效数据更新用户信息时,API 是否返回正确的错误码。
  • 强制输出格式: Prompt 需要强制 LLM 以严格的 JSON 格式输出 测试用例,避免 LLM 产生 markdown 或注释等干扰信息。
  • 利用变量提取和插值: prompt 鼓励 LLM 使用变量提取规则从 JSON 响应中提取变量(例如用户 ID),并在后续请求中使用这些变量,从而实现链式测试。
  • 指定期望的状态码: Prompt 应该明确指定每个测试步骤的期望状态码,以便 Caseforge 可以自动验证测试结果。

下面是一个 Prompt 示例,旨在指导 LLM 生成 PUT /users/{id} 端点的 测试用例

你是一名 API 测试专家。基于以下 OpenAPI 端点规范和 schemas,生成一个严格 JSON 格式的测试套件。

每个测试套件应该:
- 至少包含一个成功案例和几个失败案例
- 使用从先前步骤中提取的变量 (例如 IDs)
- 完全匹配以下 schema: {...}

OpenAPI 端点规范:
[在此处插入 OpenAPI 端点规范]

Schemas:
[在此处插入 schemas]

请以 JSON 格式返回测试套件:

即便如此,我们偶尔仍然会收到格式错误的 JSON 或不完整的步骤。我们通过使用强大的 JSON 解析、重试逻辑和低温度模型设置来缓解这种情况。

下面是 LLM 应该返回的示例。此 JSON 结构包括肯定和否定测试用例,每个测试用例由多个步骤组成。输出设计为可在 Caseforge 运行时中按原样执行:

{
  "name": "PUT /users Test Suite",
  "target_method": "PUT",
  "target_path": "/users/{id}",
  "test_cases": [
    {
      "name": "Positive: Update user information",
      "description": "Ensure that valid data successfully updates the user",
      "error_type": null,
      "test_steps": [
        {
          "method": "POST",
          "path": "/users",
          "request_body": { "name": "Taro", "email": "taro@example.com" },
          "extract_rules": { "user_id": "$.id" },
          "expected_status": 201
        },
        {
          "method": "PUT",
          "path": "/users/${{user_id}}",
          "request_body": { "name": "Taro Updated" },
          "expected_status": 200
        }
      ]
    },
    {
      "name": "Negative: Missing required field",
      "description": "Should return 400 when 'name' is missing",
      "error_type": "missing_field",
      "test_steps": [
        {
          "method": "POST",
          "path": "/users",
          "request_body": { "name": "Taro", "email": "taro@example.com" },
          "extract_rules": { "user_id": "$.id" },
          "expected_status": 201
        },
        {
          "method": "PUT",
          "path": "/users/123",
          "request_body": { "email": "invalid@example.com" },
          "expected_status": 400
        }
      ]
    }
  ]
}

端点依赖推断:构建复杂的多步骤测试场景

多步骤测试通常需要在调用一个端点之前调用另一个端点(例如,创建用户 → 创建帖子 → 删除帖子)。Caseforge 通过分析以下内容来推断此类依赖关系:

  • 路径模式: 例如,/users/{id} 意味着 POST /users 是先决条件。
  • 字段名称: 例如,authorId 暗示需要调用 POST /users
  • 响应体 schemas: 分析响应体schema中的外键关系,推断API之间的依赖关系。

这些推断出的链接被传递给 LLM,以帮助它正确地链接请求。虽然远非完美,但它对于遵循传统命名模式的 REST API 效果很好。例如,假设我们需要测试创建文章 (POST /articles) 端点,而创建文章需要提供作者 ID (authorId)。Caseforge 可以通过分析 OpenAPI schema 和 API 路径模式,自动推断出创建文章端点依赖于创建用户端点,并生成包含创建用户和创建文章两个步骤的 测试用例

POST /users
    ↓ (user_id)
POST /articles (authorId = user_id)
    ↓ (article_id)
DELETE /articles/{article_id}

替代方案比较:Caseforge 的独特优势

与模糊测试或传统的代码生成相比,Caseforge 在生成基于 OpenAPI 语义的真实、多步骤场景的能力方面脱颖而出。

  • 模糊测试: 模糊测试是一种黑盒测试技术,通过向 API 发送随机数据来发现潜在的漏洞和错误。然而,模糊测试通常无法生成有意义的 测试用例,难以覆盖复杂的业务逻辑。
  • 代码生成: 代码生成工具可以根据 OpenAPI schema 自动生成 API 客户端代码和服务器端代码。然而,代码生成工具通常无法生成完整的 测试用例,需要人工编写测试代码。

Caseforge 结合了 OpenAPI schema、RAG 和 LLMs 的优势,可以自动生成包含正向和负向测试、多步骤依赖关系的 测试用例,从而显著提高测试效率和测试覆盖率。

反思与展望:持续改进 API 自动化测试

虽然远非完美,但这种方法显示出真正的潜力:

  • 它大大减少了测试编写者的样板工作。
  • 生成的场景通常会捕获重要的边缘案例。
  • 将 OpenAPI 与 RAG 结合使用为 LLM 提供了急需的基础。

但它也有局限性:

  • 输出质量对 prompt 工程很敏感。
  • 端点之间的依赖关系有时会遗漏。
  • 链式的执行逻辑可能会变得脆弱。

未来,我们将继续改进 Caseforge,包括:

  • 优化 Prompt 设计: 通过优化 prompt 设计,提高 LLM 生成 测试用例 的质量和覆盖率,特别是针对身份验证流程和边缘情况。
  • 支持多 Schema 上传: 支持上传多个 OpenAPI schema,以便更好地测试微服务架构。
  • 输出适配器: 提供多种输出适配器,将生成的 测试用例 转换为 Postman 或 Playwright 等流行测试工具的格式。

例如,我们可以设计更加精细的 prompt,指导 LLM 生成针对 OAuth 2.0 身份验证流程的 测试用例,验证 API 在不同授权模式下的行为。此外,我们还可以开发一个 Postman 适配器,将 Caseforge 生成的 测试用例 转换为 Postman 集合,方便开发人员在 Postman 中执行和调试测试。

AI 辅助测试不是要取代 QA 工程师,而是要为他们提供更好的构建块。我们希望 Caseforge 能够激发更多在这个领域的实验。AI 在软件测试领域的应用前景广阔,值得我们持续探索和研究。希望 Caseforge 能为 API 测试用例 自动生成领域带来一些新的思路和启发。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注