你把功能 vibe coding 出来了,可它真的测过吗?
你敲了一句话。二十分钟后,一个能用的结账流程、一个设置页、一个新的 dashboard 组件就都有了。AI 写好了,你读了足够多的 diff、信得过它,就发布了。这是 vibe coding 真正兑现的那一面:构建的阻力基本消失了。
然后你打开应用检查一下。把新流程点一遍,该干的事都干了,你就往下走了。这一遍点击就是全部的测试计划。感觉没问题,那就算完事了。
发布变简单了,信心没有。
问题会在你第三、第四次这么干的时候冒出来。你每加一个功能,就多一条用户能走的路径;你每改一次东西,都可能弄坏上周加的某条路径。周一点过的那条流程,如今成了周四改的三样东西的下游。你没有再点它,因为还有另外十一条流程等着,真点起来能点一整天。
于是你干脆不再检查了。这不是一个主动的决定,而是必然的结果:手动验证的成本随功能数量的平方往上涨,你的耐心却是一条水平线。你听说的第一个回归来自某个用户,而且就出在一条你造出来那天还好好的流程里。
手动点击和手写 spec,在这儿都不行
显而易见的答案是自动化测试:写 Playwright spec,在 CI 里跑,赶在用户之前抓住回归。想法没错,然后它一头撞上了墙。
功能不是你手写的,你也不会手写四十个 spec 去覆盖它。写一个 Playwright 测试,意味着要学 selector、学 wait、学断言、学 setup,然后为一堆你压根没敲过的代码,把这些一丝不苟地做一遍。这笔账是倒着的。你当初选 vibe coding,图的就是跳过那个慢的部分,而手写测试又把那个慢的部分原封不动搬了回来——只不过这回是无聊的那种慢。
摆在桌面上的两个选项是:手动点击,撑不住规模;手写 spec,你又不会去做。于是大多数 vibe coding 应用最后两样都没有,全凭感觉发布,一碰就碎。
用你 vibe coding 的方式去 vibe test 它
解法是用你构建的方式来测试。你当初用大白话描述功能,把它做了出来,那就用大白话描述流程,把它验证一遍。
这正是 Hover 干的事。它是一个免费、开源的 MCP server,加进你已经在用的编码 agent 就行。你提一个类似「登录、往购物车加一件商品、用一张存好的卡结账」的要求,agent 就会驱动你真实的 Chrome 精确地把这件事做出来。它搭在你机器上现成的 claude 或 codex CLI 上,通过 CDP 连到你真实的浏览器。没有新账户,没有独立的测试框架,也没有对你应用的 headless 模拟。它就在你自己用来点点点的那个浏览器里跑流程,所以它验证的,正是用户真正会碰到的东西。
你在旁边看着它跑。流程能用,就留下;不能用,那你就赶在用户之前找到了 bug——而这本来就是全部的意义。
你最后得到的是什么
运行成功后,Hover 把它 crystallize 成一个普通的 @playwright/test spec 文件。不是一段绑死在 Hover 上的录制,也不是一个非要 AI 才能重放的东西,而是你能读、能改、能提交的标准 Playwright 代码。
那个 spec 会在 CI 里永远跑下去,回路里没有任何 AI。agent 只在编写时出手一次,就像手写测试要是够快、你也会亲手去写那样。此后它就是一套普通的 Playwright suite:没有 API 调用,没有每次运行烧掉的 token,也没有「模型每次自己决定点哪儿」带来的非确定性。关于为什么 AI 应该编写测试、却绝不该在 CI 里运行它,我们另有专文,而这条界线就是整个设计的核心。
于是循环闭合了。你 vibe code 一个功能,vibe test 它,然后带走一个真正的 spec,它会在每次 push 时守住那条流程。加第十一个功能,不再意味着要把前十个重新点一遍——那件事现在交给 CI 了。
测试是 vibe coding 藏起来的第一笔成本。接下来两笔是:随着应用变大让它一直能用(为什么 vibe coding 应用总是坏),以及确认它可以安全发布(vibe coding 留下的安全漏洞)。快速构建已经解决了,下一个问题是你能不能信任自己造出来的东西——剩下的活儿,全在这里。
在你自己的应用上试试 Hover。
把 Hover 的 MCP 加到你已经在用的编码 agent。它探索你的应用,结晶出你自己拥有的纯 Playwright spec。
npm i -g @hover-dev/mcp && claude mcp add hover -- hover-mcp阅读快速开始 →