内容
# AI 测试飞行员
> 一个由 LLM 驱动的测试生成器,具有共享核心和可插拔适配器。指向 Python 模块或网页;它会内省目标,让 LLM 提出测试场景作为模式验证的 JSON,将它们渲染为可运行的测试,运行它们,并对失败进行分类。


[](https://github.com/Drzymek92/ai-test-pilot/actions/workflows/ci.yml)

**为什么它与众不同:** 大多数“AI 编写测试”工具让模型直接生成测试代码 — 这会产生导入幻觉、构造输入和断言错误。AI 测试飞行员采取相反的立场:LLM 只返回**结构化、模式验证的 JSON**;每行可运行代码都从该 JSON **确定性地** 渲染。LLM 仅用于两个真正模糊的步骤 — *提出* 场景和 *判断* 模糊失败 — 而不是其他。
## 它真的能捕获错误吗?
覆盖率是一个弱代理 — 一个测试套件可以执行每行代码,但断言没有有用的东西。测试生成器的真正测试是**杀死率**:从*正确*代码生成一个测试套件,然后在*有错误*的版本上运行它,并计算测试通过正确代码后现在捕获的错误数量(标准突变测试语义)。完整的重现性评估在 [`benchmark/`](benchmark/DETECTION.md) 中提供。
| Corpus | Kill rate (95% Wilson CI) | Notes |
|---|---|---|
| **QuixBugs** (外部,默认配置) | **0.80** [0.584–0.919] (n=20) | 人工验证的正确↔错误对 |
| **存储库内 AST 突变** (完全消融) | **0.818** [0.523–0.949] (n=11) | 受控突变;“是否过度设计?”消融基板 |
| **HumanEval 保留 — 标准工具(cosmic-ray)** | **0.923** [0.906–0.937] (n=1166) | 使用标准突变工具在开发过程中从未见过的代码重新测量 |
**与基于搜索的同行相比:** 在相同的 QuixBugs 目标和相同的杀死机制上,**我们的 0.80 vs Pynguin 的 0.30** (Pynguin 在适度的 30s/SIMPLE 预算下 — 基线;即使文献中更强的 SBST 范围,~0.59–0.70,也低于 0.80)。优势在于**覆盖率反馈循环** — 在第一次传递后,测试套件的未覆盖行被反馈给 LLM 以达到它们 — 这是 LLM 测试生成所缺乏的反馈机制。
**诚实的框架:** 那个保留数字最初使用此存储库自己的轻量级突变运算符(0.98)进行测量 — 一个泛化检查,**不是** PIT 风格的突变分数 — 因此它被重新框架并**重新测量为标准工具(0.923)** 以获得可比较的数字;cosmic-ray 不排除等效突变,因此 0.923 是一个保守的下限,而更难、更具代表性的数字仍然是 QuixBugs **0.80**。方法论、可比较性与文献以及重新运行程序: **[benchmark/DETECTION.md](benchmark/DETECTION.md)**。
```bash
python scripts/main.py detect --subset 20 # QuixBugs + 存储库内突变杀死率 + 功能消融
```
## 演示 — 示例运行
```text
$ python scripts/main.py --target path/to/rules/commission.py --selector compute_commission --golden
内省了 1 个单元;解析类型:OrderView, LineItemView, RulesConfig, CommissionRules
生成了 5 个场景
黄金模式:锁定了 5 个特征断言
运行完成:5/5 通过
✓ 5 通过 · 0 失败 · 0 错误 / 5 生成
测试: scripts/outputs/tests/test_commission_<ts>.py
报告: scripts/outputs/reports/report_<ts>.md
```
生成的测试构造真实的输入并锁定计算结果:
```python
def test_standard_commission():
"""多个商品订单的佣金。"""
result = compute_commission(
order=OrderView(currency="PLN", status="DELIVERED", line_items=[
LineItemView(category="electronics", unit_amount=Decimal("100.00"), quantity=2)]),
config=RulesConfig())
assert repr(result) == ("CommissionBreakdown(currency='PLN', "
"items_commission=Decimal('4.00'), transaction_fee=Decimal('1.00'), "
"total_commission=Decimal('5.00'), rule_version='v1')")
```
对于 `web_playwright` 适配器,相同的管道生成自包含的 Playwright 测试和惯用的 `.spec.ts` 导出。包括三个示例目标:`demo/signup.html` (简单表单)、`demo/login_app/` (提供 — 认证/存储状态 + API 拦截)和 `demo/ws_app/` (提供 — WebSocket 推送/回声)。使用 `--serve` 运行演示:
```bash
# 深度网页:发出 base_url + auth_state 固定件、页面路由拦截、异步变体
python scripts/main.py --adapter web_playwright --target demo/login_app/index.html --serve
# WebSocket:发出页面路由 WebSocket 模拟(服务器推送 + 回声)+ expect_ws_message
python scripts/main.py --adapter web_playwright --target demo/ws_app/index.html --serve
```
## 功能
- **结构化输出管道** — LLM 返回针对 Pydantic 模式验证的 JSON;每行测试代码都从验证对象中**确定性地** 渲染,而非由模型编写。
- **类型化输入构造** — 通过 `ast` 从源代码中解析参数**类型** (数据类/Pydantic/ `attrs` / `NamedTuple` ,嵌套) **而无需导入目标** ,因此它测试真正的领域/面向对象代码,而不仅仅是接受基本类型的函数。
- **可复制和安全** — `temperature=0` + 场景缓存重放相同的测试,对于未更改的目标;LLM 调用具有超时/重试和文档化的 **退出代码契约** ,因此它永远不会发出半生成的测试套件。
- **已证明的错误检测** — 一个 `detect` 命令测量 **突变杀死率** (不是仅仅覆盖率),具有功能消融和 **覆盖率反馈循环** ,而普通的 LLM 测试生成缺乏这些。
- **高级 Playwright(提供模式)** — `base_url` / `auth_state` 固定件, `storage_state` 重用, `page.route` 网络拦截,以及进程内 WebSocket 模拟 — 所有这些都来自 LLM 发出的 JSON。
- **MCP 服务器 + 质量/成本门控** — 可从任何 MCP 客户端调用;质量回归门控(假阳性率,测试气味密度...)和可选的预算上限,在过度消费之前中止。
<details>
<summary><b>完整功能详情</b> (点击展开)</summary>
- **结构化输出管道** — LLM 返回针对 Pydantic 模式验证的 JSON(具有一次性修复重试);代码从验证对象中生成,而非由模型编写。值文法的 `$type` / `$call` / `$enum` 符号(和构造函数参数名称) **仅限于从目标自己的源代码解析的类型** ,因此精心制作的文档字符串/源代码不能将代码令牌偷偷带入生成的测试中,然后执行。
- **类型化输入构造** — 通过 `ast` 递归解析函数的参数**类型** 从源代码中(数据类 + Pydantic + `attrs` + `NamedTuple` ,嵌套, `Decimal` / `datetime` / `Enum` ,默认值) **而无需导入目标** ,并构建真正的构造函数调用。它甚至公开 Pydantic 字段约束( `gt` / `le` / `min_length` , `Annotated[...]` )以便模型选择 **有效** 值而不是触发构造时验证错误。使其测试领域/面向对象代码,而不仅仅是接受基本类型的函数。
- **可复制生成** — 生成在 `temperature=0` 下运行,并缓存每个场景集,键入提示 + 解析的模型版本 + 温度,因此重新运行未更改的目标会重放 **相同的** 测试,零令牌成本;模型更改会使缓存失效。 `--no-cache` / `--refresh-cache` 覆盖它。
- **来自源代码的断言强度** — 单元自己的代码被输入到生成中(一个有界切片),因此断言针对 **特定的计算行为** ,而不仅仅是类型/形状。 `--golden` 然后锁定真实结果;它们一起将草稿变成回归守卫。
- **特征(黄金)模式** — 运行每个调用并锁定断言为真实结果,将生成的测试变成回归守卫。防止时间炸弹:它双运行并仅保留可复制的结果,跳过任何时钟/ RNG 读取单元,其时间未固定。
- **文件和固定件输入** — 为文件处理函数创建真实的临时文件,并且可以选择从配套的 [合成数据工厂](https://github.com/Drzymek92/synthetic-data-factory) 中播种输入。
- **故障分类** — 确定性信号表为免费分类大多数故障( `bad_scenario` / `env_issue` / 破坏的黄金锁定 → `real_bug` );LLM 仅用于真正模糊的故障。
- **高级 Playwright(提供网页模式)** — 固定件( `base_url` , `auth_state` ),认证会话通过保存的 `storage_state` ,网络拦截( `page.route` )以确定性地模拟 API,进程内 WebSocket 模拟( `page.route_web_socket` ,服务器推送 + 回声),以及 `async_playwright` 变体。每个都只是 LLM 发出的结构化 JSON — 模型中没有 Playwright 代码。
- **自我跟踪账本 + 自我改进调优** — 每个运行都记录到 DuckDB; `accept` 回填了保留了多少测试。工具然后提出最佳提示版本 *和* (在 `auto` 模式下)将您以前接受的场景作为少镜头示例注入,用于相同的目标作为少镜头示例 — 在 **零额外 LLM 调用** 下关闭循环。
- **草稿 → 套件工作流** — `discover` 扫描项目并打印每个模块的即用命令; `promote` 删除草稿的样板,重新编写黄金锁定为值断言,并将非重复测试仅追加到现有的套件中。两者都是确定性的,零令牌。
- **MCP 服务器** — 将引擎公开为工具( `introspect` , `generate_tests` , `triage_failures` , `run_metrics` , `accept_run` )以便它可以从任何 MCP 客户端调用。
- **合同安全** — LLM 调用具有超时 + 指数退避重试,并且在发出半生成的测试套件之前引发,而不是 **退出代码契约** ( `0` ok · `1` 内部 · `2` 用法 · `3` 无法检查的目标 · `4` LLM 失败 · `5` 质量回归 · `6` 预算超额 · `7` 检测回归)用于脚本。
- **质量回归门控** — `quality` 运行一个精选的已知良好目标集并报告指标面板(覆盖率,通过率,假阳性率,错误率,测试气味密度,接受率),针对存储的基线进行门控,以便生成/提示更改如果质量回归则大声失败。
- **成本防护** — 每个运行都衡量和记录真实的令牌花费(缓存重放是免费的);可选的 `[budget]` 上限在超支前中止,并且 `sweep` “测试差异”仅为 git 更改的模块生成,在每个扫描上限下。
</details>
相同的引擎通过一个 **适配器缝隙** 驱动两种目标类型,因此添加一种新类型目标只需一个新文件,无需更改核心:
- **`python_pytest`** — 指向 Python 模块,生成 `pytest` 测试。
- **`web_playwright`** — 指向网页,生成 Playwright 端到端测试(并导出惯用的 TypeScript)。
## 工作原理
```mermaid
flowchart LR
T([目标:模块或 URL]) --> I[1 · 内省<br/>ast / DOM — 确定性]
I --> G[2 · 生成<br/>LLM → 模式验证的 JSON]
G --> M[3 · 物化<br/>渲染代码 — 确定性]
M --> R[4 · 运行<br/>pytest / Playwright]
R --> TR[5 · 分类<br/>信号 + LLM 用于模糊]
TR --> L[6 · 记录<br/>DuckDB 账本]
L -. 7 · 提出调优 .-> G
```
阶段 1、3、4、6 的成本为 **零令牌** 。阶段 2 是一个批处理 LLM 调用;阶段 5 仅对确定性信号表无法分类的故障调用 LLM。核心永远不会直接导入适配器 — 仅通过名称注册 — 这使得两种目标类型完全解耦:
```mermaid
flowchart TD
C[共享核心<br/>内省 · 生成 · 物化 · 运行 · 分类 · 记录] --> RG[注册表]
RG --> A1[python_pytest 适配器]
RG --> A2[web_playwright 适配器]
A1 --> P[(pytest)]
A2 --> PW[(Playwright + TS 导出)]
```
## 技术栈
- **语言:** Python 3.10+
- **核心:** `pydantic` (模式主干), `jinja2` (pytest 发射), `duckdb` (运行账本)
- **LLM:** `langchain-openai` 针对任何 OpenAI 兼容网关( `LLM_BASE_URL` / `LLM_MODEL` / `LLM_API_KEY` )
- **适配器:** `pytest` (Python 目标运行器), `playwright` (网页 — 捆绑其自己的驱动程序,无需 Node)
- **集成:** `mcp` (FastMCP)— 通过模型上下文协议公开引擎
## 入门指南
### 先决条件
- Python 3.10+ 在 PATH 上
### 安装
```bash
git clone https://github.com/Drzymek92/ai-test-pilot.git
cd ai-test-pilot
python -m venv .venv
.venv\Scripts\activate # Windows (在 macOS/Linux 上使用 source .venv/bin/activate)
pip install -e . # 安装依赖 + `ai-test-pilot` 命令
# (或仅使用 `pip install -r requirements.txt` 依赖项,而不带控制台入口点)
cp config/.env.example config/.env # 然后填写您的 LLM 网关值
# 仅适用于网页适配器:
python -m playwright install chromium
```
安装后,您可以将其称为 `ai-test-pilot ...` ,在任何地方;下面的示例使用 `python scripts/main.py ...` ,这等效。
### 用法
```bash
# 为选定的函数生成 pytest 测试
python scripts/main.py --target path/to/module.py --selector func_a,func_b
# 将断言锁定为真实结果(特征 / 回归模式)
python scripts/main.py --target path/to/module.py --golden
# 为网页生成 Playwright 测试
python scripts/main.py --adapter web_playwright --target path/to/page.html
# 记录提出的测试中保留了多少个(调优输入)
python scripts/main.py accept <run_id> --kept 4
# 扫描项目以查找可测试的目标(确定性,无 LLM)
python scripts/main.py discover path/to/project
# 增量:仅 git 更改的模块(在 git 中更改的模块重新生成测试,而不是整个树)
python scripts/main.py discover path/to/project --changed # 工作树 vs. HEAD
python scripts/main.py discover path/to/project --since main # vs. 参考/标签/提交
# 清理草稿以适应套件:删除样板,重写黄金锁定,并将非重复测试追加到现有套件中
python scripts/main.py promote <run_id> --into tests/test_module.py
# 强制新鲜(缓存)生成,或重新生成 + 覆盖缓存
python scripts/main.py --target path/to/module.py --no-cache # 跳过场景缓存
python scripts/main.py --target path/to/module.py --refresh-cache # 重新生成并存储
# 质量门控:运行精选集,打印指标面板,如果回归则失败(退出 5)
python scripts/main.py quality # 与基线对比
python scripts/main.py quality --update-baseline # (重新)设置基线
# “测试差异”:仅为 git 更改的模块生成,在预算上限下
python scripts/main.py sweep path/to/project --since main
```
将 **MCP 服务器** (可从任何 MCP 客户端调用) — 注册:
```json
{ "command": "python", "args": ["/path/to/ai-test-pilot/scripts/mcp_server.py"] }
```
## 项目结构
```
scripts/
main.py # CLI + run_pipeline() (每个接口重用的管道)
mcp_server.py # MCP 服务器(FastMCP)公开引擎作为工具
cli.py # 参数解析 + 子命令调度 + 退出代码契约
pipeline.py # run_pipeline() — 每个接口重用的管道(类型化 RunRequest 输入)
core/ # 适配器无关引擎:模型,生成,物化,运行器,分类,
# 账本,调优,上下文,固定件,注册表,发现,促进,报告,
# 反馈,缓存,错误(退出代码),质量(门控),预算,检测
adapters/ # python_pytest · web_playwright (每种目标类型一个文件)
prompts/ # 场景生成提示 + pytest Jinja 模板
config/ # ai_test_pilot.toml(默认值)+ .env.example
benchmark/ # 可复制的功效评估:突变杀死率,消融,cosmic-ray +
# Pynguin 基线,Wilson CI,语料库加载器,DETECTION.md + 工件
demo/ # signup.html · login_app/(身份验证)· ws_app/(WebSocket)— 网页适配器目标
tests/ # 204 个单元测试
```
## 设计笔记
- **确定性优先。** 内省、代码生成、运行、分类信号表和账本都是普通代码。LLM 仅用于两个不可简化为模糊的步骤。
- **从不导入目标。** 内省仅基于 `ast` ,因此目标的重/可选依赖项从不被触发以生成测试。
- **人机协作。** 生成的测试被提议到 `scripts/outputs/` — 从不写入目标存储库。将其推广是一个单独的显式步骤。
在类型化的业务规则引擎、纯数据转换帮助器、网页表单、具有 API 拦截的身份验证应用和 WebSocket 源上进行了端到端验证 — 在每种情况下,都生成了可运行、正确类型、回归级别的测试。
> **CI 注意:** 公布的 CI 运行完整的单元测试套件,由于设计原因,该测试套件是浏览器无关的 — `web_playwright` 测试仅在生成的测试源上断言,而不使用实时浏览器。 `--serve` 演示在本地运行(在 `playwright install chromium` 之后);CI 不会下载浏览器。
## 限制
`python_pytest` 适配器是一种经过测试、质量门控、成本受限的工具,我依赖它来进行自己的管道 — 可复制(温度 0 + 场景缓存),故障安全(类型化退出代码契约),并由回归门控保护。其公开范围是 **受信任的单机** 代码:它执行生成的测试并将内省的源代码发送到配置的 LLM 网关,因此它不是不受信任目标的沙箱。 `web_playwright` 适配器仍然是 **演示** 高级 Playwright 技术。输入塑造了工具不会猜测的工具,非确定性 LLM 步骤,规模/安全/包装边界都写在 **LIMITATIONS.md** — 了解它们是正确使用它的部分。
## 许可
在 MIT 许可下许可 — 参见 [LICENSE](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器