python -m venv .venv
# Windows: .venv\Scripts\Activate.ps1
# macOS/Linux: source .venv/bin/activate
pip install -r requirements.txt
pytest -q- Import:
docs/postman/postman_collection_day3.json - Import env:
docs/postman/postman_environment_dev.json - Select env:
api-test-dev - Run: Collection Runner → verify all assertions pass
This project migrates a Postman collection (10+ requests) into a pytest-based API automation suite.
- Data-driven test cases (
data/postman_migrated_cases.json) - Reusable HTTP client (
core/http_client.py) - Environment-based configuration (
config/env.py) - One-command run:
python -m pytest -q
# optional: override environment variables
$env:BASE_URL="https://jsonplaceholder.typicode.com"
$env:TOKEN="fake_token_123"
$env:TIMEOUT="10"
python -m pytest -q- Skills: SELECT / JOIN / GROUP BY / HAVING
- Output:
docs/sql_validation_cases.md(10 cases + SQL) - Demo DB:
reports/demo.db(SQLite)
python tools/seed_sqlite_from_api.py目标:能用固定流程定位接口异常/慢:curl 复现 → 端口/进程 → 日志 → 结论/修复 → 回归验证
交付物:docs/debug_sop.md(排障 SOP)
- 复现与耗时(Reproduce & Measure)
curl -i <url>curl -v <url>curl -s -o /dev/null -w "%{http_code} %{time_total}\n" <url>
- 端口/进程(Port & Process)
ss -lntp | head -n 50ss -lntp | grep ':<port> 'sudo lsof -i :<port> | headps -p <pid> -o pid,user,cmd --no-headerssystemctl status <service> --no-pager -n 60
- 日志(Logs)
sudo journalctl -u <service> -n 120 --no-pagersudo journalctl -u <service> --since "today" --no-pager | grep -nEi "error|warn|tls|handshake" | tail -n 120
- 特征:网关日志出现
connect: connection refused/dial tcp ... refused - 验证:
ss -lntp | grep ':8000 ' || echo "8000 not listening"curl -s -o /dev/null -w "%{http_code} %{time_total}\n" http://127.0.0.1:8000/ || true
- 修复(systemd):
sudo systemctl daemon-reloadsudo systemctl enable --now <service>sudo journalctl -u <service> -n 120 --no-pager(失败时查原因)
- 回归:
ss -lntp | grep ':8000 'curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://api.<domain>/ || true
- 现象:
curl https://127.0.0.1/https://localhost报TLS alert internal error - 关键点:
-H "Host: ..."不会改变 TLS SNI - 正确本机验证(推荐):
curl -Iv --resolve www.<domain>:443:127.0.0.1 https://www.<domain>/curl -Iv --resolve api.<domain>:443:127.0.0.1 https://api.<domain>/
- 可选修复:需要本地也能
https://localhost时,在 Caddyfile 增加localhost, 127.0.0.1 { tls internal ... }
- 形成结构化排障 SOP:
docs/debug_sop.md - 能用证据链解释:
- 502:upstream down / port not listening
- TLS:SNI 与 Host header 区别,
--resolve模拟真实域名
完成接口测试基础能力建设,包括:
- API 测试方法
- Postman 断言
- SQL 数据验证
- Linux 排障基础
- 测试思维总结
功能:
- pytest 参数化
- requests 调用 API
- status code 断言
- JSON字段断言
当前用例数量:
10+
将用例中的 requests.get/post 统一封装为 client.get/post,做到:
base_url统一管理- 复用
requests.Session()(连接复用/性能更好) - 用例更短、更易维护(更接近企业项目写法)
新增模块:
config/config.py:集中管理BASE_URL、默认超时等配置core/client.py:封装APIClient,提供request/get/post方法
用例改造:
tests/test_posts.py从requests.get(url)改为client.get(path)
api-test-framework
├─ tests/
│ └─ test_posts.py
├─ core/
│ └─ client.py
├─ config/
│ └─ config.py
├─ data/
├─ reports/
└─ requirements.txt
将测试用例中的“裸断言”抽象为统一的断言工具(Assertion Helpers),让用例更短、更一致、更易维护,并且在失败时输出更可读的错误信息(包含 url/status/body/json 等关键上下文)。
新增模块:
core/assertions.pyassert_status(resp, expected):状态码断言(失败时打印 url/status/body)assert_status_in(resp, {..}):状态码集合断言(适用于 200/404 等多分支预期)assert_json_has_keys(resp, keys):JSON 必要字段存在断言assert_json_value(resp, key, expected):JSON 字段值断言assert_json_path_value(resp, "a.b.c", expected):嵌套字段 JSON Path 断言(可选)- 内部工具:
_safe_json/_safe_text(JSON 解析失败也能输出可读信息)
用例改造:
tests/test_posts.py- 将
assert r.status_code == ...、r.json()["xx"] == ...替换为断言层函数 - 删除不再使用的
requestsimport - 对不确定返回(如 200/404)使用
assert_status_in
- 将
api-test-framework
├─ tests/
│ └─ test_posts.py
├─ core/
│ ├─ client.py
│ └─ assertions.py
├─ config/
│ └─ config.py
├─ data/
├─ reports/
└─ requirements.txt
Step 1:安装依赖
pip install pyyamlStep 2:实现数据加载器
创建:core/data_loader.py
支持读取 .yaml/.yml/.json
校验 cases 必须存在且非空
提供 to_parametrize() 输出 (cases, ids)
Step 3:改造/新增数据驱动用例
创建:tests/test_posts_ddt.py
pytest.mark.parametrize("case", cases, ids=ids)
用 APIClient().request(...) 发请求
用 assertions.py 的断言函数做校验
Step 4:运行与验证
pytest -q