Claude Code Plugins

Community-maintained marketplace

Feedback

使用 pytest + httpx 进行 API 接口自动化测试的工具包。支持 RESTful API 测试、JWT 认证、参数化测试、断言验证以及测试报告生成。

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name api-testing
description 使用 pytest + httpx 进行 API 接口自动化测试的工具包。支持 RESTful API 测试、JWT 认证、参数化测试、断言验证以及测试报告生成。

API 接口自动化测试

要测试后端 API 接口,请编写原生的 Python pytest + httpx 脚本。

可用辅助脚本

  • scripts/api_client.py - API 客户端封装(支持认证、请求、响应处理)
  • scripts/with_backend.py - 管理后端服务器生命周期

务必先使用 --help 运行脚本 以查看用法。在你先尝试直接运行脚本并确认必须定制之前,不要阅读源码。这些脚本可能非常庞大,会污染你的上下文窗口。

决策树:选择你的方法

用户任务 → 是否有 OpenAPI/Swagger 文档?
    ├─ 是 → 读取 /docs 或 /openapi.json 获取接口定义
    │         ├─ 成功 → 根据接口定义编写测试脚本
    │         └─ 失败 → 手动分析接口(见下)
    │
    └─ 否 → 后端服务是否已在运行?
        ├─ 否 → 运行:python scripts/with_backend.py --help
        │        然后使用该助手启动服务 + 编写测试脚本
        │
        └─ 是 → 先探测,后测试:
            1. 获取 OpenAPI 文档:GET /openapi.json
            2. 分析接口路径、方法、参数
            3. 编写测试用例覆盖正向/异常场景
            4. 执行测试并验证响应

项目服务配置

本项目的服务端口配置:

服务 地址 说明
后端 API http://localhost:5000 FastAPI 服务
API 文档 http://localhost:5000/docs Swagger UI
OpenAPI http://localhost:5000/openapi.json OpenAPI 规范

示例:使用 with_backend.py

要启动后端服务,先运行 --help,然后使用该助手:

启动后端服务:

python scripts/with_backend.py --server "python run.py" --port 5000 -- pytest tests/api/ -v

指定工作目录:

python scripts/with_backend.py \
  --server "python run.py" \
  --port 5000 \
  --cwd platform-fastapi-server \
  -- pytest tests/api/test_login.py -v

示例:使用 api_client.py

编写测试脚本时,使用封装好的 API 客户端:

from scripts.api_client import APIClient

# 创建客户端
client = APIClient(base_url="http://localhost:5000")

# 登录获取 Token
client.login("admin", "admin123")

# 发送请求
response = client.get("/user/queryById", params={"id": 1})
assert response.status_code == 200

data = response.json()
assert data["code"] == 200
assert data["data"]["username"] == "admin"

# 关闭客户端
client.close()

"先探测,后测试"模式

  1. 获取接口文档

    response = client.get("/openapi.json")
    api_spec = response.json()
    paths = api_spec["paths"]  # 所有接口路径
    
  2. 分析接口定义

    # 获取某个接口的详细信息
    login_spec = paths["/login"]["post"]
    request_body = login_spec["requestBody"]
    responses = login_spec["responses"]
    
  3. 编写测试用例

    def test_login_success():
        response = client.post("/login", json={
            "username": "admin",
            "password": "admin123"
        })
        assert response.json()["code"] == 200
    

pytest 测试脚本模板

# tests/api/test_user.py
import pytest
from scripts.api_client import APIClient

class TestUserAPI:
    """用户管理接口测试"""
    
    @pytest.fixture(autouse=True)
    def setup(self):
        """测试前置:创建客户端并登录"""
        self.client = APIClient()
        self.client.login()
        yield
        self.client.close()
    
    # ========== 正向测试 ==========
    
    def test_query_user_list(self):
        """查询用户列表"""
        response = self.client.post("/user/queryByPage", json={
            "pageNum": 1,
            "pageSize": 10
        })
        assert response.status_code == 200
        data = response.json()
        assert data["code"] == 200
        assert "list" in data["data"]
    
    # ========== 异常测试 ==========
    
    def test_query_user_not_found(self):
        """查询不存在的用户"""
        response = self.client.get("/user/queryById", params={"id": 99999})
        data = response.json()
        assert data["data"] is None or data["code"] != 200
    
    # ========== 参数化测试 ==========
    
    @pytest.mark.parametrize("username,password,expected", [
        ("admin", "admin123", True),      # 正确凭证
        ("admin", "wrong", False),         # 错误密码
        ("", "admin123", False),           # 空用户名
    ])
    def test_login_scenarios(self, username, password, expected):
        """登录场景参数化测试"""
        client = APIClient()
        response = client.post("/login", json={
            "username": username,
            "password": password
        })
        data = response.json()
        assert (data["code"] == 200) == expected
        client.close()

常见陷阱

❌ 忘记在请求头中添加 Token ✅ 使用 client.login() 自动管理 Token

❌ 硬编码测试数据导致用例相互依赖 ✅ 每个测试用例独立创建和清理数据

❌ 只测试正向场景 ✅ 覆盖正向、异常、边界值场景

❌ 忽略响应状态码,只检查业务 code ✅ 同时验证 HTTP 状态码和业务响应码

最佳实践

  • 使用 fixture 管理客户端生命周期 - 避免重复创建/销毁客户端
  • 参数化测试 - 使用 @pytest.mark.parametrize 减少重复代码
  • 数据隔离 - 测试数据使用唯一标识,测试后清理
  • 断言明确 - 验证状态码、响应结构、关键字段
  • 分层组织 - 按模块组织测试文件,便于维护

测试执行命令

# 运行所有 API 测试
pytest tests/api/ -v

# 运行指定测试文件
pytest tests/api/test_login.py -v

# 运行指定测试类/方法
pytest tests/api/test_user.py::TestUserAPI::test_query_user_list -v

# 生成 HTML 报告
pytest tests/api/ -v --html=reports/api_report.html

# 并行执行
pytest tests/api/ -v -n auto

# 失败重试
pytest tests/api/ -v --reruns 2

参考文件

  • examples/ - 展示常见模式的示例:
    • basic_crud_test.py - 基础 CRUD 接口测试
    • auth_test.py - 认证授权测试
    • parametrize_test.py - 参数化测试示例
    • data_driven_test.py - 数据驱动测试

完整教程

examples/tutorial/ - 9个渐进式示例,从入门到高级:

入门级(1小时)

示例 目录 学习内容
01 beginner/01_basic_request/ GET/POST 请求、响应获取
02 beginner/02_response_validation/ 状态码、JSON 结构验证
03 beginner/03_authentication/ 登录、Token 认证

中级(1.5小时)

示例 目录 学习内容
04 intermediate/04_crud_testing/ CRUD 接口测试、数据清理
05 ⭐ intermediate/05_parametrize/ 参数化测试(重点)
06 ⭐ intermediate/06_data_driven/ 数据驱动测试(重点)

高级(2小时)

示例 目录 学习内容
07 advanced/07_contract_testing/ OpenAPI 契约验证
08 advanced/08_performance/ 响应时间、并发测试
09 ⭐ advanced/09_comprehensive/ 完整测试套件(重点)

运行教程

cd examples/tutorial

# 查看所有命令
make help

# 运行特定示例
make 01  # 基础请求
make 05  # 参数化测试(重点)
make 09  # 综合套件(重点)

# 运行所有示例
make all

核心知识点速查

参数化测试(重要):

@pytest.mark.parametrize("username,password,expected", [
    ("admin", "admin123", True),   # 正确凭证
    ("admin", "wrong", False),      # 错误密码
    ("", "admin123", False),        # 空用户名
])
def test_login(self, username, password, expected):
    response = client.post("/login", json={
        "username": username,
        "password": password
    })
    actual = response.json()["code"] == 200
    assert actual == expected

数据驱动测试(重要):

TEST_DATA = [
    {"case_id": "TC_001", "username": "admin", "password": "admin123", "expected": True},
    {"case_id": "TC_002", "username": "admin", "password": "wrong", "expected": False},
]

@pytest.mark.parametrize("case", TEST_DATA, ids=lambda x: x["case_id"])
def test_login(self, case):
    response = client.post("/login", json={
        "username": case["username"],
        "password": case["password"]
    })
    assert (response.json()["code"] == 200) == case["expected"]

Fixture 数据清理:

@pytest.fixture
def cleanup_users(api_client):
    created_ids = []
    yield created_ids
    # 测试结束后自动清理
    for id in created_ids:
        api_client.delete(f"/user/delete", params={"id": id})

详细内容请查看 examples/tutorial/README.md