Skip to main content
Glama

TestTrout

面向 AI 构建应用的测试助手。

你可以把它当作一个应用来运行,也可以通过 MCP 交给你的编码代理。无论哪种方式,它都会为你写出应用从未有过的测试,并在测试失败时及时告知你。

License Python Status


鳟鱼是指示物种。它们只生活在清澈、含氧量高的水中——如果你在溪流中发现一条鳟鱼,不用亲自检测就知道水质是健康的。

一套好的测试套件正是如此:它不是一件苦差事,而是一个一眼就能读懂的信号。


问题所在

编码代理交付速度很快,但也会悄无声息地破坏东西,而且它们构建的应用——Lovable、v0、Bolt,以及在 Vercel 上手工搭建的 React 应用——几乎从来都没有测试。于是整个循环以写出代码的那个家伙的一句"看起来没问题"告终。

TestTrout 补上了这个闭环。它读取代码仓库,连接正在运行的部署,找出哪些功能未被测试以及按什么顺序测试最重要,编写真正的测试,运行它们,并报告你可以真正据此行动的结果。

Related MCP server: Lastest

让它跑起来

pip install testtrout
trout up

存储、一个 worker,以及位于 localhost:7411 的界面。无需 Docker、无需守护进程、无需安装数据库——SQLite 位于 ~/.testtrout 下,worker 在进程内运行。不需要任何 API 密钥。 这里没有任何东西调用模型。

工作原理

添加一个项目——本机上的一个文件夹,以及它部署所在的 URL。

trout add ~/code/my-app --url https://my-app.vercel.app

然后是四个步骤,也就是四条命令:

trout look     # read the code, ask the deployment, work out what is testable
trout facts    # what I still need from you — all optional
trout build    # write the baseline and prove it
trout run      # re-run it and report what changed

基线就是你的部署当前的行为。 TestTrout 记录下这些行为,并断言它们持续保持。它并不知道当前行为是否正确——没有人告诉过它——它也永远不会问。它只知道行为是什么,并在行为发生变化的那一天察觉出来。

正是这一条规则,决定了为什么没有访谈、没有审批队列、也没有关于你产品的一系列问题。它唯一会问的,是它无法自行发现的那些具体值:

它要求提供

原因

部署 URL

仅凭代码无法测试任何东西

API URL

仅当你的后端部署在独立主机上时

一个账号

仅当某个请求确实拒绝了未认证的访问时

一个真实 ID

仅当探针无法自行访问 /jobs/:id 时

每一项都能在几秒内回答,每一项都是可选的,而且每一项都会说明它解锁了什么。只给它一个 URL 而什么都不给,你就能得到所有在未登录状态下加载的页面——对大多数应用来说,这已经是一套有用的测试套件了。

它整理出的所有内容都会作为工件保存在侧边栏中——项目地图、表单、测试计划、基线套件——这样重要的东西就不会埋在三层聊天记录之上。

或者留在你的编码代理里

应用只是入口之一,不是唯一入口。它做的每一件事都以类型化的 MCP 工具和 CLI 命令的形式提供,所以任何不想离开编辑器的人也不必离开:

pip install 'testtrout[mcp]'
trout mcp /path/to/your/project

把你的代理指向 .claude/skills/ 中的技能,或者告诉它:

使用 TestTrout MCP 服务器。查看代码仓库,告诉我它还需要我提供什么,然后构建基线并运行它。

七个工具,绑定到一个项目上,这样代理就不会对错误的仓库采取行动:

look

阅读代码,询问部署,找出哪些是可测试的。

facts set_facts

缺少哪些具体值;保存用户提供的内容。

plan

现在可以测试什么,以及每个被阻塞的项需要什么。

build

编写基线并对照部署进行验证。

run suite

重新运行它,并读取每个测试在做什么。

另外还有 trout://map、trout://facts、trout://plan、trout://config、trout://scenarios 作为资源,这样大量状态就不会挤占代理的上下文窗口。

它能理解什么

trout look 在完全不访问网络的情况下读取代码,所以即使是你刚克隆的仓库也可以安全使用。在一个典型的 Supabase 应用上,它能发现:

表面

示例

页面

/orders/:id → OrderDetail,以及它访问的数据

数据操作

supabase.from('orders').delete().eq('id', …)

RLS 策略

Users manage own orders —— 一个可测试的授权声明

服务器动作

'use server' 函数——看起来像辅助函数的端点

路由处理器

app/api/checkout/route.ts → POST

第三方服务

Stripe、Resend——替换边界

模式

表、列、外键、RLS 状态

它还会告诉你,什么时候一个表是从浏览器代码写入的,且没有行级安全——这意味着任何人都可以通过 anon 密钥写入。这通常是个新闻。

它从不要求你的数据库

测试访问你的应用的方式和用户一样:通过 HTTP 访问你的端点,用真实浏览器访问你的界面。登录走的是你自己的登录表单,trout look 会定位一次,这样测试就能重放已知的表单,而不是去猜测。

所以整个事情只需要一个 URL 和两个测试账号。不需要 anon 密钥、不需要 service role、不需要项目凭据——没有任何大多数团队会合理拒绝交给测试工具的东西。

授权仍然被覆盖,而且可以说覆盖得更好:以两个普通账号登录,一个能看到的,另一个必须看不到。这既测试了你的策略,也测试了你的应用层在策略之上叠加的一切——这才是真实用户会遇到的东西。

为什么这些测试值得信任

它构建的是基线,而不是每个 PR 的猜测。 从你刚改的代码推导出的测试,断言新行为正确是构造性的——它无法捕捉回归。TestTrout 首先对照一个正在运行的部署来认证测试套件,所以失败意味着真有问题。

任何地方都没有模型。 扫描中没有、规划中没有、编写测试中也没有。每一条断言都追溯到部署实际做过的事情,这就是为什么相同的输入总是产生相同的测试套件,也是为什么不需要配置任何 API 密钥。

每条断言都带有其来源,在基线中它永远是 observed——证据就写在生成的文件里:

// observed: the page title when /orders was loaded
await expect(page).toHaveTitle('Orders');
// observed: seen on /orders at baseline
await expect(page.getByTestId('orders-table')).toBeVisible();

它只发送 GET 请求。 端点测试重放探针发出的请求,无论端点声明了什么方法,所以把它指向生产环境也不会改变那里的任何东西。

失败在报告之前就被分类了。 只有 assertion_failure 是产品信号。认证失败、数据库不可达、第三方调用被阻断,这些都属于测试框架本身的问题,而且一次不确定的运行绝不会被升级为通过。

一切都可以从终端完成

trout add ~/code/my-app --url https://my-app.vercel.app
trout look          # read the code, ask the deployment
trout facts         # what I still need — all optional
trout build         # write the baseline and prove it
trout run           # re-run it; a failure means behaviour changed

每条命令都支持 --json。完整教程见 docs/setup.md。

它如何存储数据

你的测试套件保留在你的代码仓库中,可提交、可审查:

.trout/scenarios/*.yaml    what each test asserts, in plain language
.trout/facts.yaml          what was asked for — never a secret value
.trout/plan.yaml           what can be tested, and what is waiting
.trout/config.yaml         deployments and env: references, never secrets
tests/trout/               generated Playwright and Vitest files

运行历史、随时间变化的覆盖率,以及任务队列,都存放在 ~/.testtrout 下的 SQLite 中。这种拆分是刻意的:测试套件应该放在代码旁边,让拉取请求可以审查它;而文件无法回答的问题——这个测试是不是越来越不稳定、覆盖率是不是在上升——则属于数据库。

可选的 Web 视图

trout web

覆盖率一目了然、排序后的差距列表、场景审查、带证据的运行历史、实时日志。与 CLI 使用相同的 .trout/ 文件——没有数据库、没有托管、仅回环。完全可选;CLI 和 MCP 本身已经完整。

安全性

这个工具需要你的数据库凭据,并且可以驱动你的部署,所以这些保证是在代码中强制执行的,而不是仅仅写在文档里:

  • 生产环境默认只读。 变更请求在网络层被阻止,除非某个入口被明确标记为 disposable。这道防护位于导航之下,因为"只是加载一个页面就是只读的"这个说法是错的——很多这类应用在挂载时就会写入。没有任何代理,也没有任何网页点击,能改变这个设置。

  • 第三方服务被拦截。 一次测试运行不能扣款,也不能给客户发邮件。未匹配的出站请求会大声失败;一个静默匹配不到任何东西的 mock,正是测试套件显示绿色但实际上什么都没测的方式。

  • 密钥不会进入已提交的文件。 .trout/config.yaml 只包含 env: 引用。值存放在被 gitignore 的 .env 中。

  • 没有任何托管,也没有任何东西离开你的机器。 没有要调用的模型,没有要创建的账号。没有遥测。

支持的栈

React + Vite(Lovable、v0、Bolt)和 Next.js App Router,使用 TypeScript,搭配 Supabase,部署在任何可通过 HTTP 访问的地方。认证通过 Supabase、Clerk 或 NextAuth。

刻意保持狭窄。在一个栈上做深,胜过在多个栈上做浅——而且这些代码库足够规整,静态分析在它们上面是真正准确的。其他栈只需要一个适配器:见 docs/adapters.md。

路线图

✅

仓库分析、部署探测、差距排序

✅

场景编写、生成、执行、认证

✅

MCP 服务器、CLI,以及带存储和 worker 的本地应用

✅

基于变更的测试选择、基分支差异

✅

多仓库:链接本地文件夹,或使用 GitHub token 克隆

✅

从界面或 CLI 进行完整配置,支持凭据发现

🔜

GitHub 拉取请求检查

🔜

观察覆盖率索引(当前的选择使用声明覆盖率)

贡献

添加一个框架、认证提供方或测试运行器,意味着实现一个协议并注册一个入口点——无需 fork。见 CONTRIBUTING.md 和 docs/adapters.md。

完整的测试套件在离线状态下运行,不需要 API 密钥。这是刻意的,值得保持。

许可证

Apache 2.0

Related MCP Connectors

Related MCP Servers