Skip to main content

openapi-crud-gen

从 OpenAPI 3.x 描述文件生成 pytest + requests 的 CRUD 测试骨架。

openapi-crud-gen 是一个纯静态代码生成 CLI:它读取 OpenAPI 3.0 / 3.1 文档(YAML 或 JSON), 识别 RESTful 资源及其 CRUD 操作,为每个资源生成一个可直接被 pytest 收集的测试模块。 生成过程不发起任何 HTTP 请求,业务数据与断言细节以 TODO 形式留给使用者补齐。

适用于契约测试、测试左移、CRUD 自动化与 CI 质量门禁的起点搭建。

快速上手(60 秒)

想跟着一步步走(真实输出、为什么必须填 TODO、怎么接进 CI),看 docs/TUTORIAL.md。

pip install -e .                                                    # 1. 安装(pytest/requests 已在核心依赖里)
openapi-crud-gen examples/openapi.yaml --output-dir tests/generated # 2. 生成骨架
python examples/mock_api.py                                         # 3. 另开一个终端:起个假后端
pytest tests/generated                                              # 4. 看到 5 passed

第 3 步只是让你立刻看到"生成的东西真的能跑"。真实使用时跳过它,把 --base-url 指向你自己的服务,并把生成文件里的 TODO(路径参数、请求体、断言)补齐。

生成结束后 CLI 会直接打印这三步提示,不用记:

Generated 2 file(s) in tests/generated (0 skipped, 0 endpoint(s) ignored)

Next steps:
  1. Fill in the TODOs in the generated files (path parameters, request payload, assertions).
  2. Start your API at http://localhost:8000, or regenerate with a different --base-url.
  3. Run: pytest tests/generated

常见问题

跑生成出来的测试报 ConnectionError: [WinError 10061] 由于目标计算机积极拒绝 生成的是会真实发请求的骨架,BASE_URL 上必须真的有人监听。启动你的后端,或先 python examples/mock_api.py;也可以换地址重新生成:--base-url http://127.0.0.1:9000。

requests 报 ModuleNotFoundError requests 已经随本工具一起装好,出现这个错说明跑 pytest 的解释器不是装工具的那个。 在跑测试的环境里执行 pip install openapi-crud-gen(源码目录用 pip install .)。

为什么路径参数是 id = None 骨架不知道你的真实 ID。写成 None 是为了避免运行时 NameError,跑之前替换成真实值。

状态码断言失败:文档写 [201],接口却返回 200 断言取自文档声明的响应码。接口实现和文档不一致时,改文档或改断言,别把断言删成 assert response.status_code。

文档里声明了 404/400,为什么断言里没有 只自动断言 2xx/3xx,避免断言恒真失去意义。需要时在 # TODO: 补充响应体断言 处自己加。

只想看会生成什么、不写文件 加 --dry-run。

重新生成时提示 skipped ... (exists) 默认不覆盖已有文件,确认要覆盖就加 --overwrite;手写的断言不会被悄悄冲掉。

安装

需要 Python 3.10+。

pip install openapi-crud-gen     # 工具 + pytest + requests,一条命令把环境装好

pytest 与 requests 是核心依赖(生成的测试要用它们跑),装完就能直接 pytest tests/generated,不会出现 ModuleNotFoundError: No module named 'requests'。

从源码装(仓库尚未发布到 PyPI 时)

pip install .            # 在项目根目录执行
pip install -e .         # 要改代码时用可编辑安装
pip install ".[dev]"     # 额外装上 pytest-cov,用于跑覆盖率门槛
uv pip install -e .      # 用 uv 也一样

pip install openapi-crud-gen 要等包上传 PyPI 后才有效;发布准备已完成(PEP 639 元数据、 twine check 双 PASSED、wheel 内含模板),发布命令见下面的"发布到 PyPI"。

使用

openapi-crud-gen [OPTIONS] OPENAPI_FILE
选项 默认值 说明
--output-dir PATH tests/generated 生成文件输出目录,不存在则创建
--base-url TEXT http://localhost:8000 写入生成文件 BASE_URL 常量的值
--resource-filter TEXT 无 逗号分隔资源名,仅生成匹配资源;未匹配项只告警
--overwrite 关闭 覆盖已存在文件;关闭时跳过并提示
--dry-run 关闭 只打印将要生成的文件,不写盘
--version — 打印版本号并退出

退出码:0 成功(含“无可生成资源”);1 文件无法解析或不是 OpenAPI 3.x; 2 参数错误(如文件不存在)。OpenAPI schema 校验失败只输出警告,不影响生成。

示例输入

examples/openapi.yaml:

openapi: 3.0.3
info:
  title: User API
  version: 1.0.0
servers:
  - url: http://localhost:8000
paths:
  /users:
    get:
      summary: 获取用户列表
      responses:
        '200':
          description: OK
    post:
      summary: 创建用户
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
      responses:
        '201':
          description: Created
  /users/{id}:
    get:
      summary: 获取用户详情
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
    put:
      summary: 更新用户
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: OK
    delete:
      summary: 删除用户
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: No Content

运行

openapi-crud-gen examples/openapi.yaml --output-dir tests/generated --base-url http://localhost:8000
Loaded examples/openapi.yaml (OpenAPI 3.0.3)
                        Recognized CRUD resources
+-----------------------------------------------------------------------+
| Resource | Base path | Actions                                        |
|----------+-----------+------------------------------------------------|
| users    | /users    | create, read_list, read_detail, update, delete |
+-----------------------------------------------------------------------+
Generated 2 file(s) in tests/generated (0 skipped, 0 endpoint(s) ignored)

生成结果

tests/generated/
├── __init__.py            # 输出目录缺少时自动生成
└── test_users_crud.py     # 每个资源一个文件

tests/generated/test_users_crud.py:

"""CRUD test skeleton for the users resource.

Generated by openapi-crud-gen from an OpenAPI document. Fill in every TODO
before running these tests against a live service.
"""
import pytest
import requests

BASE_URL = "http://localhost:8000"


def test_users_create():
    # TODO: 准备请求数据
    response = requests.post(f"{BASE_URL}/users", json={})
    assert response.status_code in [201]
    # TODO: 补充响应体断言


def test_users_read_list():
    response = requests.get(f"{BASE_URL}/users")
    assert response.status_code in [200]
    # TODO: 补充响应体断言


def test_users_read_detail():
    id = None  # TODO: 替换路径参数
    response = requests.get(f"{BASE_URL}/users/{id}")
    assert response.status_code in [200]
    # TODO: 补充响应体断言


def test_users_update():
    id = None  # TODO: 替换路径参数
    # TODO: 准备请求数据
    response = requests.put(f"{BASE_URL}/users/{id}", json={})
    assert response.status_code in [200]
    # TODO: 补充响应体断言


def test_users_delete():
    id = None  # TODO: 替换路径参数
    response = requests.delete(f"{BASE_URL}/users/{id}")
    assert response.status_code in [204]
    # TODO: 补充响应体断言

生成的文件可以立即被 pytest 收集:

pytest --collect-only tests/generated

CRUD 识别规则

资源名取路径的第一段:/users/{id} → users。路径形态只有两种参与识别:

形态 判定 例
Collection 仅一个非参数段 /users
Detail 一个非参数段 + 恰好一个路径参数段 /users/{id}
动作 key 方法 + 形态 默认状态码(文档未声明 2xx/3xx 时)
create POST + Collection 201
read_list GET + Collection 200
read_detail GET + Detail 200
update PUT 或 PATCH + Detail 200
delete DELETE + Detail 204

补充约定:

  • 状态码取自文档中该操作声明的 200–399 码,升序去重;4xx/5xx 不写入断言(避免断言恒真)。
  • PUT 与 PATCH 同时存在时 PUT 优先,另一个计入 ignored。
  • POST/PUT/PATCH(或文档声明了 requestBody 的操作)生成 json={} 占位。
  • 非 CRUD 形态(/users/{id}/posts、/users/me、/{tenant}/users、根路径)以及 HEAD/OPTIONS/TRACE 不生成用例,只在摘要中计入 ignored。
  • 资源名规范化为合法标识符:user-profiles → user_profiles,2fa → r_2fa,class → class_。

Python API

from openapi_crud_gen.crud import identify_crud
from openapi_crud_gen.generator import generate_tests
from openapi_crud_gen.parser import extract_endpoints, load_openapi, validate_spec

spec = load_openapi("openapi.yaml")
warnings = validate_spec(spec)                  # 只返回消息,不抛异常
resources = identify_crud(extract_endpoints(spec))
files = generate_tests(resources, "tests/generated", "http://localhost:8000")

限制与已知问题

  • 只生成骨架:不执行真实请求,不生成认证、数据准备、数据清理逻辑。
  • 只识别一级扁平资源,不支持嵌套/子资源与自定义动作(如 POST /users/{id}/activate)。
  • 生成的用例相互独立,create 返回的 id 不会传给 read_detail/update/delete。
  • $ref 参数与 components 复用不会展开,请求体只生成 {} 占位。
  • --base-url 只取命令行值,不读取文档 servers(保持与默认值行为一致)。
  • 不支持 Swagger 2.0;servers 变量模板不解析。
  • 资源名规范化可能把两个不同路径段折叠为同名资源(/user-profiles 与 /user.profiles)。

开发指南

pip install -e ".[dev]"
pytest                                                # 跑测试
pytest --cov=openapi_crud_gen --cov-fail-under=80     # 加上覆盖率门槛(CI 用这条)
  • 结构:parser.py(加载与展平)→ crud.py(识别资源与动作)→ generator.py + templates/test_crud.py.j2(渲染)→ cli.py(命令行与摘要)。
  • 测试:tests/fixtures/ 提供 YAML 与 JSON 示例文档;CLI 测试使用 click.testing.CliRunner; 测试不发起任何网络请求。
  • 覆盖率门槛 80%(分支覆盖)由显式命令与 CI 强制;普通 pytest 不注入 --cov, 这样按快速上手直接 pytest tests/generated 不会被覆盖率门槛误伤。
  • CI:.github/workflows/test.yml 在 push / PR 上运行 Python 3.10、3.11、3.12 矩阵, 包含全量测试与一次 CLI 冒烟(生成 + pytest --collect-only)。

发布到 PyPI(可选)

python -m pip install build twine
python -m build                  # 产物在 dist/:wheel + sdist
python -m twine check dist/*     # 两个都要 PASSED
python -m twine upload dist/*    # 需要 PyPI API token
pip install openapi-crud-gen          # 发布后从 PyPI 复验一次

发布前清单:

  • pyproject.toml、src/openapi_crud_gen/__init__.py、CHANGELOG.md 三处版本号一致
  • pytest --cov=openapi_crud_gen --cov-fail-under=80 通过
  • twine check dist/* 全 PASSED
  • 补 [project.urls](仓库 / Issues 地址)与作者信息,目前刻意留空以免写错地址
  • 打 tag:git tag v0.1.0 && git push origin v0.1.0

License

MIT,见 LICENSE。

Metadata

Release files for openapi-crud-gen 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openapi-crud-gen 0.1.0
File Size Uploaded
openapi_crud_gen-0.1.0.tar.gz 33.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openapi-crud-gen 0.1.0
File Interpreter ABI Platform
openapi_crud_gen-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 49.1 kB

Release files / openapi_crud_gen-0.1.0.tar.gz

Download URL openapi_crud_gen-0.1.0.tar.gz
Size 33.3 kB
Tags Source
SHA-256 checksum
How to use checksums
cf4464e931650877dfcafa4ea49584c1cdafac369b6675021fc6be2687b316f1
BLAKE2b-256 checksum
How to use checksums
796063e198180eb34117ff102cd39aac916714591ba28849a76df38cf957d019
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / openapi_crud_gen-0.1.0-py3-none-any.whl

Download URL openapi_crud_gen-0.1.0-py3-none-any.whl
Size 15.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
21418bb87b04bc5152123f981dd96ae09f534c1a4a4fb6348184605e6867c2ec
BLAKE2b-256 checksum
How to use checksums
ce39959926433959609a365c2d9e0fba5782a7c9b3fb6b2d224d1e1905a7c657
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page