根本原因是 chromedriver 与 chrome 版本不匹配或启动参数不当;需确保主版本一致、显式指定 no-sandbox 等选项、禁用沙箱与 gpu,并在 ci 中限制资源、加大超时、避免竞态检测。

Go 里用 Selenium 启动 Chrome 总是卡住或报 connection refused
根本原因不是 Go 代码写错了,而是 Selenium 的 chromedriver 和本地 Chrome 版本不匹配,或者没正确传参。Go 的 selenium 官方客户端(github.com/tebeka/selenium)默认走 HTTP 协议跟 WebDriver 通信,一旦 chromedriver 没起来、端口被占、或 Chrome 自动更新后版本跳变,立刻失败。
- 启动前先手动跑一次
chromedriver --version,再对比google-chrome --version,二者主版本号必须一致(比如都是 124.x) - Go 中启动时显式指定
chromeOptions,禁用沙箱和 GPU,否则 Linux 环境下常静默崩溃:caps.AddChromeOption("args", []string{ "--no-sandbox", "--disable-gpu", "--headless=new", // 新版 headless 必须加 =new }) - 别依赖全局 PATH 下的
chromedriver;用wd, err := selenium.NewRemote(caps, "http://localhost:9515/wd/hub")时,确保chromedriver是以--port=9515显式启动的
Rod 库里 Page.MustElement 频繁 panic,但页面明明有元素
Rod 默认不等 DOM 就查元素,MustElement 是“找不到就 panic”,不是“等不到就重试”。它适合确定已渲染完的场景,比如点击后同步出现的弹窗;但对异步加载、SPA 路由跳转、React/Vue 动态挂载的内容,几乎必崩。
- 改用
Page.Element+WaitE组合,例如:el, err := page.Element("button#submit") if err != nil { el, err = page.WaitE("button#submit", rod.EvalOnStart) } if err != nil { panic(err) } -
rod.EvalOnStart表示“只要 JS 执行过就认为存在”,比单纯查 DOM 更可靠;若要等文本内容出现,用rod.EvalOnEnd - 避免在
Page.MustNavigate后立刻调MustElement;Rod 不自动等页面 load,得手动page.WaitLoad()或page.WaitStable()
Go E2E 测试里怎么 mock 接口请求,又不让 Rod/Selenium 知道
Rod 支持拦截并响应网络请求,Selenium 则做不到——这是 Rod 的关键优势。但很多人误以为拦截后就能直接改返回体,其实 Rod 的 router.MustAdd 只能返回预设响应,没法读原始请求 body 做动态判断。
- 用
router.MustAdd匹配 URL 正则,返回固定 JSON:router := page.Router() router.MustAdd("https://api.example.com/users", func(ctx *rod.Rod) { ctx.Resp.SetBody(`{"id": 1, "name": "mock"}`) }) - 若需根据请求参数返回不同数据,得自己起一个轻量 HTTP server(如
net/http),然后把 Rod 的请求代理过去,再用router.MustAdd重定向到本地地址 - Selenium 用户只能退回到浏览器插件方案(如 Puppeteer 的
page.setRequestInterception),或改用 Rod —— Go 生态里目前没成熟替代
CI 环境跑 E2E 测试失败率高,本地却总成功
核心差异就两点:资源限制(CPU/内存)和显示环境。GitHub Actions、GitLab CI 默认无图形界面,且容器内存常低于 2GB,Chrome 启动后极易 OOM 或超时。
立即学习“go语言免费学习笔记(深入)”;
- 强制 Chrome 使用低开销模式:
--single-process --disable-dev-shm-usage --disable-extensions - 加大超时:Rod 的
page.Timeout(30 * time.Second),Selenium 的wd.SetCommandTimeout(30 * time.Second) - 别在 CI 里用
go test -race跑 E2E;竞态检测本身吃资源,且和浏览器并发模型冲突,容易假失败 - 截图调试很重要:Rod 可
page.Screenshot(&rod.ScreenshotParams{}),Selenium 用wd.Screenshot(),失败时立刻存图,比日志直观得多
真实项目里最常被忽略的是 Chrome 更新策略——CI 镜像里的 Chrome 版本半年不更新,而 chromedriver 每月一版,版本错位是静默失败的头号原因。










