Back to skills
SKILL.md
Api Testing 2
ASecurity使用 pytest + httpx 进行 API 接口自动化测试的工具包。支持 RESTful API 测试、JWT 认证、参数化测试、断言验证以及测试报告生成。
- 2 stars
- 0 votes
- 0 copies
- 0 views
- Added September 27, 2026
Works with
Security analysis
100/100Pro scans all 20 files and shows the line behind each finding
npx -y skills add David-Li0406/meta-skill-evloving --skill api-testing-2 --agent claude-codeAre you the author of Api Testing 2?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/david-li0406-api-testing-2)---
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`,然后使用该助手:
**启动后端服务:**
```bash
python scripts/with_backend.py --server "python run.py" --port 5000 -- pytest tests/api/ -v
```
**指定工作目录:**
```bash
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 客户端:
```python
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. **获取接口文档**:
```python
response = client.get("/openapi.json")
api_spec = response.json()
paths = api_spec["paths"] # 所有接口路径
```
2. **分析接口定义**:
```python
# 获取某个接口的详细信息
login_spec = paths["/login"]["post"]
request_body = login_spec["requestBody"]
responses = login_spec["responses"]
```
3. **编写测试用例**:
```python
def test_login_success():
response = client.post("/login", json={
"username": "admin",
"password": "admin123"
})
assert response.json()["code"] == 200
```
## pytest 测试脚本模板
```python
# 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` 减少重复代码
- **数据隔离** - 测试数据使用唯一标识,测试后清理
- **断言明确** - 验证状态码、响应结构、关键字段
- **分层组织** - 按模块组织测试文件,便于维护
## 测试执行命令
```bash
# 运行所有 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/` | **完整测试套件(重点)** |
### 运行教程
```bash
cd examples/tutorial
# 查看所有命令
make help
# 运行特定示例
make 01 # 基础请求
make 05 # 参数化测试(重点)
make 09 # 综合套件(重点)
# 运行所有示例
make all
```
### 核心知识点速查
**参数化测试(重要):**
```python
@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
```
**数据驱动测试(重要):**
```python
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 数据清理:**
```python
@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`。
Files in this skill
- SKILL.md
- examples/auth_test.py
- examples/basic_crud_test.py
- examples/data_driven_test.py
- examples/parametrize_test.py
- examples/tutorial/Makefile
- examples/tutorial/README.md
- examples/tutorial/advanced/07_contract_testing/test_contract.py
- examples/tutorial/advanced/08_performance/test_performance.py
- examples/tutorial/advanced/09_comprehensive/conftest.py
- examples/tutorial/advanced/09_comprehensive/test_auth.py
- examples/tutorial/advanced/09_comprehensive/test_project.py
- examples/tutorial/advanced/09_comprehensive/test_user.py
- examples/tutorial/beginner/01_basic_request/test_basic.py
- examples/tutorial/beginner/02_response_validation/test_validation.py
- examples/tutorial/beginner/03_authentication/test_auth.py
- examples/tutorial/intermediate/04_crud_testing/test_crud.py
- examples/tutorial/intermediate/05_parametrize/test_parametrize.py
- examples/tutorial/intermediate/06_data_driven/test_data_driven.py
- scripts/api_client.py
Attribution
Comments
Loading comments…