郭立 (leeguoo)

# 给前端写「单元测试」:chrome-use test 详解

chrome-use test 把手动的浏览器点检固化成能重复跑、能进 CI 的 YAML 套件,相当于给前端写单元测试。讲清它是什么、为什么实现能这么薄,以及怎么配合 cookie-use 多账号来回切换。

2026年7月1日 · 文章 · 公开

本页目录

后端代码有单元测试兜底,改一行不慌。前端呢?很多时候还停在"打开页面、点几下、瞄一眼对不对"的阶段。每次发版前手动重来一遍,枯燥,还容易漏。

chrome-use 里有个 test 命令,就是把这套手动动作固化成一份能重复跑、能进 CI 的套件——相当于给前端页面写单元测试。群里有人用完的原话是"很好用",所以单独写一篇讲清楚它是什么、怎么做到的、以及实际怎么用。

一份浏览器测试报告:两条通过、一条失败并附现场截图

它是什么

一句话:用 YAML 描述"打开哪些页面、做哪些操作、期望是什么",然后 chrome-use test suite.yaml 一把跑完,全绿就是没问题,红了就告诉你哪条断言挂了。

一份最小的套件长这样:

$ yaml
# smoke.yaml
suite: chatgpt smoke
setup:
  - account: chatgpt/huayue          # 可选:注入一个 cookie-use 里存好的登录态
cases:
  - name: 首页带登录态能打开
    steps:
      - open: https://chatgpt.com/
      - wait: { load: networkidle }
    assert:
      - url: { contains: chatgpt.com }
      - visible: "#prompt-textarea"
  - name: 输入框能收字
    steps:
      - fill: { sel: "#prompt-textarea", text: "hi" }
    assert:
      - text: { sel: "#prompt-textarea", contains: hi }
      - eval: "!!window.__NEXT_DATA__"

跑起来:

$ bash
chrome-use test smoke.yaml                     # 起一个隔离浏览器,跑完自动关
chrome-use test smoke.yaml --session default   # 或者直接在你连着的真实 Chrome 上跑

输出就是一份测试报告:

suite: chatgpt smoke  (session cu-test)
  ✓ 首页带登录态能打开   1.2s
  ✗ 输入框能收字         0.8s
      assert text "#prompt-textarea" contains "hi" → got ""
      ↳ cu-test-artifacts/composer-takes-text.png
2 cases · 1 passed · 1 failed

有一条挂了,退出码就非 0,直接丢进 CI 当门禁用。挂掉的用例还会自动存一张现场截图到 cu-test-artifacts/,方便回头看当时页面成什么样了。

步骤支持的动词:openclickfilltypepresswaitscrolleval。断言支持:urlvisiblehiddentextcounteval。基本覆盖了"操作 + 校验"的日常需要。

原理:为什么它能这么轻

test 的实现只有很薄一层,没有另起炉灶做一套测试 DSL 引擎。它的取巧之处在于三点。

原理示意:YAML 套件交给 runner,每步 re-invoke 一次 chrome-use 命令,经常驻 daemon 的 socket 打到浏览器;断言统一编译成一个 eval,读回 true/false

第一,每一步都是复用 chrome-use 自己的命令。 YAML 里的 openclickfill 这些键,直接对应 chrome-use 已有的子命令。runner 跑到某一步,就是拿这一步的参数去 re-invoke 一次 chrome-use 二进制。好处是:launch 模式、daemon、@ref 引用、各种 flag 的语义,全部原样继承,不用在测试层重新实现一遍。chrome-use 本体支持的,套件里立刻就能用。

第二,断言最后都编译成一个 truthy 的 eval 不管你写的是 visibletext 还是 count,runner 都会把它翻译成一句 JavaScript 表达式,丢进页面里求值,读回一个布尔值——真就过,假就挂。所以断言体系不需要一堆专门的判定逻辑,一个 eval 通道就把所有检查统一了。

第三,daemon 常驻,每步只是一次 socket 往返。 一个套件跑下来,浏览器会话是复用的:daemon 在整个 session 期间一直开着,每个 step 只是往 daemon 发一条命令、拿回结果,速度很快,不会每步都重启浏览器。

默认行为是起一个全新的隔离浏览器(cu-test 会话),跑完就关,这样每次跑的环境干净、结果可复现,适合 CI。如果你想在真实登录态下测,--session <name> 就能挂到已经连上的 Chrome 上(配合 chrome-use extension connect)。需要登录的站点,setup 里写一行 account: <id>,就会把 cookie-use 里存好的那份登录注入进测试会话,套件全程带着登录态跑。

实际怎么用

真正让它有价值的不是"一次写一大堆用例",而是把它嵌进你本来就在做的事里:

  1. 先手动确认一次。open / snapshot / eval 把页面点一遍,顺便把选择器摸清楚:哪个元素、什么文案、什么 URL。
  2. 写成一条 case。 把刚才的操作和期望落成 YAML 里的一个用例。
  3. 跑起来看颜色。 绿了说明这条链路是通的;红了会直接告诉你哪条断言没过,附一张截图。
  4. 以后遇到回归就再加一条。 每修一个 bug、每发现一个"上次好好的这次坏了",就补一条对应的 case。套件是越用越厚、越用越值钱的资产。

对经常要验证登录态、验证关键流程(能不能进首页、输入框收不收字、下单流程走不走得通)的场景,这一套能把"每次发版前手动点一遍"变成"CI 里自动跑一遍",省下来的是每次发版都要花的那点重复精力。

很多测试离不开多个账号:管理员能看到的设置项,普通成员不该看到;A 用户发的东西,B 用户那边收不收得到。这件事 chrome-use test 交给了 cookie-use——一个把各站点登录态存进加密本地库、随时取用的工具。

在套件里用 account: <id> 这一行,就能把某个存好的登录注入进测试会话。它有两个位置可以写:

  • 写在 setup 里,整份套件都以这个账号跑。
  • 写在某条 case 的 steps 里,跑到这步就切成另一个账号,切换在这条 case 下一次 open / 跳转时生效。这就是"来回切换":一份套件里,一条用例用管理员、下一条用普通成员、再下一条切回管理员。
$ yaml
setup:
  - account: myapp/admin           # 先以管理员身份起
cases:
  - name: 管理员能看到设置入口
    steps:
      - open: https://app.example.com/
    assert:
      - visible: "#settings-tab"
  - name: 普通成员看不到
    steps:
      - account: myapp/member      # 切成成员,再打开页面
      - open: https://app.example.com/
    assert:
      - hidden: "#settings-tab"
  - name: 切回管理员
    steps:
      - account: myapp/admin
      - open: https://app.example.com/
    assert:
      - visible: "#settings-tab"

前提是装了 cookie-use、账号也存了进去。切换失败(id 写错、没装 cookie-use)会让那条 case 直接失败并说明原因,不会静默跳过。其中 setup 注入从 test 上线就有,case 内切换是 v1.5.58 加的。

当前边界

当前版本(v1)还比较克制:断言是在所有步骤跑完之后统一求值的,没有单用例重试、没有并行用例、也还没有截图基线比对(要比对内容先用 eval 断言顶着)。步骤是顺序执行的,某一步挂了这条用例就立即失败。这些是已知的边界,够日常回归用了。

完整命令说明可以 chrome-use skills get test 拉到本地。两个仓库:chrome-use 在 github.com/leeguooooo/chrome-use,多账号用到的 cookie-use 在 github.com/leeguooooo/cookie-use

下一篇 →
上班摸鱼聊微信,把它搬进终端、还伪装成 dev 工具——以及背后的原理

评论

评论发布后会立即公开,如触发规则可能被审核下架。

最多 1000 字。