Selenium 开发指南:用 Bazel 与 ./go 构建、测试并发布多语言语言绑定
【免费下载链接】seleniumA browser automation framework and ecosystem.项目地址: https://gitcode.com/GitHub_Trending/se/selenium
本文基于 Selenium 官方仓库根目录的 README.md 展开,系统讲解作为 Selenium 贡献者需要准备的环境、Bazel 构建体系、各语言绑定(Java / Python / Ruby / Rust)的开发与测试流程,以及文档生成与发布(Releasing)的完整命令链路。读完后你将能够独立搭建 Selenium 本地开发环境、执行bazel build/bazel test等核心命令,并使用./goRake 封装完成从构建到发布的日常工作。
1. 这个仓库是什么:Selenium 伞项目与 W3C WebDriver 基础设施
README 开篇给出了一句最著名的口号:"Automates browsers. That's it!"。Selenium 是一个"伞项目"(umbrella project),封装了多种用于 Web 浏览器自动化的工具与库,其核心定位为W3C WebDriver 规范的基础设施实现——一个平台无关、语言无关、兼容所有主流浏览器(Chrome、Firefox、Edge、Safari、IE)的编码接口。
仓库顶层目录清晰地体现了这一"多语言绑定 + 构建基建"的组织方式:
| 目录 | 内容 |
|---|---|
java/ | Java 绑定与 Grid 服务端(selenium-server / standalone jar) |
py/ | Python 绑定(selenium 包) |
dotnet/ | .NET 绑定 |
rb/ | Ruby 绑定(selenium-webdriver gem) |
javascript/ | Node.js 绑定(selenium-webdriver)与 atoms |
rust/ | Rust 实现的 Selenium Manager |
cpp/ | 遗留的 IEDriver、webdriver-interactions 等 C++ 组件 |
common/ | 共享资源:Web 测试页面、Bidi/DevTools 协议 schema、镜像等 |
需要特别注意的是 README 的声明:这份 README 面向希望为项目贡献代码的开发者。如果你是初次使用 Selenium 的用户,应查阅官方 User Manual 获取示例与描述。本文严格沿袭这一定位,聚焦"如何在本仓库中构建与开发"。
2. 环境准备:从 Bazelisk + JDK 到 Windows 手动配置
2.1 所有平台的公共依赖
README 列出的本地开发环境依赖只有两项核心项:
- Bazelisk——Bazel 的包装器。它会自动读取仓库根目录的 .bazelversion 文件,下载其中指定的 Bazel 版本(当前仓库锁定为9.1.0),并将所有命令行参数透明地转发给真正的 Bazel 二进制。这意味着你只需安装 Bazelisk 一次,无需手动跟随项目升级 Bazel。
- Java JDK 17 或更高版本(例如 Java 17 Temurin)。注意两点:
- 必须把
JAVA_HOME指向 JDK(而非 JRE)的可执行文件位置; - 验证方法是运行
javac:如果只装了 JRE,该命令不存在;如果看到一串命令行选项输出,说明 JDK 引用正确。
- 必须把
从源码结构看,.bazelrc 中还配置了
--java_runtime_version=remotejdk_25,即 Bazel 构建过程中会拉取 JDK 25 工具链来编译目标产物(默认--release 11字节码级别),这与本机 JDK 17 的要求并不冲突:本机 JDK 供 Rake/go等脚本运行,构建工具链由 Bazel 自行管理。
2.2 macOS 平台
- 安装Xcode 及命令行工具:
xcode-select --install; - Apple Silicon 机器需要Rosetta:在
.bazelrc.local文件中添加build --host_platform=//:rosetta。
这个 Rosetta 平台在根目录 BUILD.bazel 中有对应定义——一个约束为os:osx + cpu:x86_64的platform规则,供 Apple Silicon 上以 x86_64 方式运行 host 构建时使用。README 也注明团队正致力于长期消除这一要求。
2.3 Windows 平台:自动脚本与手动配置两条路线
Windows 上 Bazel 的依赖较多(MSYS2 提供 bash、VS C++ 构建工具、长路径支持等),README 提供了两个选项:
选项 1:从零自动安装。以管理员身份打开 PowerShell 后执行:
Set-ExecutionPolicy Bypass -Scope Process -Force然后在目标目录下载并执行仓库自带的 scripts/dev-environment-setup.ps1。该脚本会保证得到一个"开箱即跑"的完整开发环境(已存在的组件不会被重复安装,除非提示)。
选项 2:手动安装。完整步骤如下:
允许运行 Selenium 仓库内的脚本:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned启用开发者模式:
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"安装MSYS2(提供类 Unix 命令行的 shell 环境):
- 把 bin 目录加入
PATH(例如"C:\tools\msys64\usr\bin"); - 把
bash.exe位置设为BAZEL_SH环境变量(例如"C:\tools\msys64\usr\bin\bash.exe")。
- 把 bin 目录加入
安装最新版Visual Studio Community:
- 通过安装器添加 "Desktop development with C++" 工作负载;
- 将 Visual C++ 构建工具安装目录设为
BAZEL_VC(例如"C:\Program Files\Microsoft Visual Studio\2022\Community\VC"); - 将 Visual C++ Build Tools 版本号设为
BAZEL_VC_FULL_VERSION(可从"$BAZEL_VC\Tools\MSVC\<BAZEL_VC_FULL_VERSION>"的目录名中查出)。
启用长文件名支持(Bazel 有大量超过 Windows 默认路径长度限制的嵌套目录):
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor" /t REG_DWORD /f /v "DisableUNCCheck" /d "1" reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /t REG_DWORD /f /v "LongPathsEnabled" /d "1"- 允许 Bazel 为长路径创建短名版本:
fsutil 8dot3name set 0 - 把 Bazel 输出目录从项目内嵌套目录改到
C:/tmp:创建selenium/.bazelrc.windows.local文件,写入startup --output_user_root=C:/tmp。
.bazelrc 顶部两行
try-import %workspace%/.bazelrc.local与try-import %workspace%/.bazelrc.windows.local正是为这类本地化覆盖文件预留的入口。- 允许 Bazel 为长路径创建短名版本:
2.4 替代开发环境:GitPod、Dev Container 与 Docker 镜像
如果不想在本地搭环境,README 提供了三种"云端/容器化"替代方案:
- GitPod:提供开箱即用的在线开发环境,无需本地配置。
- Dev Container:本质是一个 Docker 容器,仓库提供了 .devcontainer/devcontainer.json。查看该文件可以看到其构建直接指向 scripts/dev-image/Dockerfile,即 Dev Container 与 Docker 镜像两条路线复用同一份 Dockerfile。VS Code、IntelliJ IDEA 等支持 Dev Container 的 IDE 会引导你创建容器。
- Docker 镜像:直接使用 scripts/dev-image/Dockerfile 构建一个适合构建与测试 Selenium 的镜像。
2.5 多 Worktree 协作:让 Bazel 缓存"随取随用"
README 专门用一节讲Using Worktrees,这是一个很实用的贡献者技巧:Bazel 默认把构建输出和下载的依赖放在每个 checkout 之下,因此使用多个 git worktree 时会重复下载依赖、重复构建。解决办法是在用户级/path/to/your/home/.bazelrc中添加:
common --disk_cache=/path/to/your/home/.cache/bazel-disk common --repository_cache=/path/to/your/home/.cache/bazel-repo--disk_cache存储编译 action 的输出;--repository_cache存储下载的外部依赖(如http_archive压缩包)。
两个目录会随时间无限增长,磁盘紧张时应定期清理;并且建议把缓存放在与 checkout 相同的文件系统上,这样 Bazel 可以 hardlink 而非复制。
另一个容易踩的坑是output base:Bazel 会为每个 checkout 路径创建独立的 output base(编译产物、分析缓存、Bazel server),且删除 worktree 时它不会被自动清理——频繁创建/销毁 worktree 会泄漏数 GB 的陈旧输出。macOS/Linux 上可以让 worktree "自清理":在该 worktree 的.bazelrc.local中添加
startup --output_base=.local/output-base.local/目录既被 gitignore 也出现在.bazelignore中(见 .bazelignore),删除 worktree 时 output base 随之消失,而上面共享的--disk_cache/--repository_cache仍然跨 worktree 共享。Windows 用户则应继续保留startup --output_user_root=C:/tmp(如 2.3 节所述)以避免路径长度限制,不要在仓库内更深嵌套 output base。
3. 构建体系:Bazel 命令、目标命名与 ./go 封装
3.1 为什么用 Bazel
Selenium 统一使用 Bazel 作为构建工具,README 给出的理由是:用它来统一管理依赖下载、生成必需的二进制、构建与发布打包、执行测试——且"快速、高效"。
3.2 四个最常用的 Bazel 命令
README 列出的核心命令:
| 命令 | 作用 |
|---|---|
bazel build | 解析依赖、编译源文件、为指定目标生成输出文件,用于产出可执行文件、库或其他产物 |
bazel run | 先构建目标再执行它,通常用于产出可执行二进制的目标 |
bazel test | 构建并以带额外测试功能(测试运行器、沙箱、缓存)的上下文运行目标 |
bazel query | 查询给定路径下可用的目标 |
3.3 目标(target)命名规则
每个可构建模块由一个BUILD.bazel文件定义。执行模块时,目标引用格式为://+ 定义文件相对路径 +:+ 目标名。README 举的例子是构建 Grid:
bazel build //java/src/org/openqa/selenium/grid:executable-grid查看根目录 BUILD.bazel 可以看到,仓库为此提供了根级 alias,于是构建 Grid 可以简写为:
bazel build grid该 alias 的actual正是//java/src/org/openqa/selenium/grid:executable-grid,与 README 描述完全一致(构建日志会输出输出 jar 的位置)。此外 BUILD.bazel 还定义了selenium-server-standalonealias,指向//java/src/org/openqa/selenium/grid:selenium_server_deploy.jar。
按语言批量构建:
bazel build //<language>/...3.4 ./go:Rake 封装的命令入口
README 指出,很多 Bazel 命令都被自定义的 Rake wrapper 包装,通过仓库根目录的./go命令执行,例如构建某个语言的全部绑定:
./go <language>:build从源码看,go 脚本本身非常薄:它设置若干--add-opensJVM 参数后,用third_party/jruby/jruby-complete.jar启动 JRuby 并执行rake <task>;真正的任务逻辑在 Rakefile 及各语言任务文件中。Rakefile 按语言加载命名空间任务:
namespace(:java) { load 'rake_tasks/java.rake' } namespace(:rb) { load 'rake_tasks/ruby.rake' } namespace(:py) { load 'rake_tasks/python.rake' } namespace(:python) { load 'rake_tasks/python.rake' } # alias namespace(:node) { load 'rake_tasks/node.rake' } namespace(:dotnet) { load 'rake_tasks/dotnet.rake' } namespace(:rust) { load 'rake_tasks/rust.rake' }所以./go java:build、./go py:install、./go rb:console这类"语言:任务"语法,本质上是 JRuby 驱动的 Rake 任务分发。Rakefile 中还有一个值得注意的规则:任何看起来像 Bazel 目标(以//开头)的任务名,都会被直接转交给bazel build执行。此外 Rakefile 还内置了all:build/all:docs/all:release/all:format等跨语言聚合任务(例如 Rakefile 的all:build会依次调用五种语言绑定的 build 任务),这正是第 5 节发布流程的基础。
4. 分语言开发实践
4.1 Java
IntelliJ:团队日常主力 IDE。推荐安装 Bazel IJ 插件,并把仓库作为 Bazel 项目导入,view 文件使用 scripts 目录下的
ij.bazelproject(macOS/Linux)或ij-win.bazelproject(Windows)。Lint:使用 Google Java Format,配合 IntelliJ 的 Google Java Formatter 插件(配置步骤需查其官方文档);也可以直接运行自动格式化脚本
./scripts/format.sh。本地安装到 Maven 仓库:虽然 Selenium 不用 Maven 构建,但可以把构件部署到本地
~/.m2/repository供 Maven 项目使用:./go java:install更新依赖:依赖版本统一声明在 MODULE.bazel 中(bzlmod 模块,声明了 rules_java、rules_python、rules_ruby、rules_rs 等各语言 ruleset 以及 protobuf、llvm 等)。更新某个依赖版本后执行:
RULES_JVM_EXTERNAL_REPIN=1 bazel run @maven//:pin或一键自动更新并重新 pin:
./go java:update查看 MODULE.bazel 可以看到仓库对 protobuf 做了
single_version_override锁定(要求与预编译 protoc 版本配对),这类精细的版本治理都集中在这一个文件里。
4.2 Python
Lint 与格式化:遵循 PEP 8 风格指南(唯一例外:行长放宽到 120 字符),用 ruff 检查与强制;同样可用
./scripts/format.sh一键自动格式化。从源码本地运行(不打包安装):
pip install -r py/requirements_lock.txt ./go py:local_devpy:local_dev会构建生成文件(Bidi 协议类等生成代码)并拷贝到本地源码树,之后就可以从py目录直接import selenium。构建并安装 wheel:
./go py:install该命令尝试安装到全局
site-packages,可能没有写权限,README 建议在安装前创建并激活一个虚拟环境。
4.3 Ruby
替代
irb的交互式 REPL(自动加载全部 gems):bazel run //rb:console调试:使用
debuggem。步骤为:在断点位置写入
binding.break;用
ruby_debug配置运行测试:bazel test --config ruby_debug <test>;调试器启动后,在另一个终端连接:
bazel-selenium/external/bundle/bin/rdbg -A这个
ruby_debug配置并非凭空而来:查看 .bazelrc 可以看到test:ruby_debug被定义为--test_output=streamed --test_env=RUBY_DEBUG_FORK_MODE=parent --run_under="@bundle//bin:rdbg --nonstop --open --command",即通过--run_under把 rdbg 调试器挂到测试进程前。
RubyMine:把
rb/作为主项目目录打开;从selenium父目录运行./go rb:local_dev生成最新产物;在Settings / Languages & Frameworks / Ruby SDK and Gems中新增 Interpreter,指向../bazel-selenium/external/rules_ruby++ruby+ruby/dist/bin/ruby。此后即可运行与调试任意 spec,默认使用 Chrome,可通过 rb/TESTING.md 中说明的环境变量切换浏览器。
4.4 Rust
Rust 侧的 Bazel 依赖是直接从rust/Cargo.toml和rust/Cargo.lock生成的,贡献者改依赖时只需维护这两个 Cargo 文件,无需手写 Bazel 依赖规则。
5. 测试:Bazel 测试配置、过滤策略与 Linux 无头方案
5.1 常用测试参数
README 给出的一组测试常用参数(原文列表完整保留):
| 参数 | 说明 |
|---|---|
--pin_browsers=false | 改用 Selenium Manager 定位浏览器/驱动(默认使用仓库 pin 的版本) |
--headless | 以无头模式运行浏览器(支持 Chrome、Edge、Firefox) |
--flaky_test_attempts 3 | 失败的测试最多重跑 3 次 |
--local_test_jobs 1 | 控制测试并行度 |
--cache_test_results=no/-t- | 禁用测试结果缓存,全部重跑 |
--test_output all | 打印测试的全部输出,而不只是错误 |
--test_output streamed | 逐个串行运行测试并立即打印输出 |
--test_env FOO=bar | 向测试进程传递额外环境变量 |
--run_under="xvfb-run -a" | 在执行前插入的前缀命令 |
这两个自定义 flag(--pin_browsers、--headless)不是普通布尔开关,而是通过 .bazelrc 中的flag_alias映射到仓库内定义的 bool_flag。查看 common/BUILD.bazel 可以看到:pin_browsers默认值为True(测试默认用 pin 的浏览器),headless默认False,并配有对应的config_setting(use_pinned_browser/use_headless_browser),各语言测试目标据此选择驱动与浏览器启动参数。
5.2 按大小与 tag 过滤测试
Selenium 测试按 size 分为三档:
- small——通常是单元测试,不打开浏览器;
- large——通常是真正驱动浏览器的测试;
- medium——比简单单元测试复杂,但不完整驱动浏览器。
按 size 过滤:
bazel test //<language>/... --test_size_filters=small按 tag 过滤(注意负号前缀表示排除):
bazel test //<language>/... --test_tag_filters=this,-not-thisREADME 特别提醒:如果同时给出多个--test_tag_filters,只有最后一个生效——在继承了某个 config 时要格外小心。
各语言的测试细节写在对应目录的TESTING.md中,如 java/TESTING.md、py/TESTING.md、rb/TESTING.md。以 Python 为例,py/TESTING.md 展示了 Bazel 为每种浏览器生成独立测试目标:bazel test //py:unit(无浏览器单元测试)、bazel test //py:test-chrome(Chrome 浏览器测试)、bazel test //py:test-firefox(Firefox),单个测试文件的目标命名规则是test/<path>/<file>-<browser>[-variant],可用bazel query //py:all | grep <name>查全名。
5.3 Linux 上的有头测试:虚拟 X 服务器
Bazel 默认在当前 X 服务器 UI 上跑测试;没有显示器时可以用虚拟/嵌套 X 服务器:
启动 X 服务器:
Xvfb :99或Xnest :99启动窗口管理器,例如:
DISPLAY=:99 jwm运行测试:
bazel test --test_env=DISPLAY=:99 //java/... --test_tag_filters=chrome
更简单的方式是用 Bazel 的--run_under:
bazel test --run_under="xvfb-run -a" //java/...6. 文档与发布
6.1 API 文档生成
各语言绑定的 API 文档由./go <language>:docs更新,全部语言一次更新:
./go all:docs对应 Rakefile 中all:docs任务会依次调用 java/py/rb/dotnet/node 五种语言绑定的 docs 任务(见 Rakefile)。
6.2 发布(Releasing)
发布是"构建 + 发布"的组合流程,往往需要多次执行并做额外处理。README 的核心要点:
使用 Rake 任务(
./go命令)完成构建与发布,且这些./go命令会附带--stamp参数,把版本等构建信息写入产物;这与 .bazelrc 中build:release --stamp与--compilation_mode=opt的 release 配置相呼应。全量构建并发布:
./go all:release只发布指定语言:
./go <language>:release如果有 Selenium EngFlow 仓库的访问权限,可以远程构建资产并下载到本地:
./go all:release['--config', 'release']这种
./go task['arg1', 'arg2']的括号语法由go脚本支持——它会把--之前的参数视为 Rake 任务参数、--之后视为 rake flags(见 go 脚本头部注释)。完整的发布流程步骤在官方 wiki 的 "Releasing Selenium" 页面。
7. 快速上手清单
结合 README 与仓库实际内容,一条最短的开发者上手路径是:
- 安装 Bazelisk 与 JDK 17+(设置好
JAVA_HOME,用javac验证); - 在目标目录克隆仓库;macOS 执行
xcode-select --install,Windows 跑 scripts/dev-environment-setup.ps1 或按 2.3 节手动配置; - 构建一个目标验证工具链:
bazel build grid(日志中会给出 Grid jar 的输出位置); - 跑一个小规模的单元测试确认环境闭环:
bazel test //py:unit或bazel test //java/... --test_size_filters=small; - 日常开发使用
./go <language>:build/./go <language>:docs,格式化跑./scripts/format.sh; - 需要多 worktree 时,按 2.5 节配置用户级
--disk_cache/--repository_cache与 per-worktree 的output_base。
本文全部内容以当前仓库快照为准:Bazel 版本 9.1.0(.bazelversion)、JDK 17+ 环境要求、pin_browsers默认开启等细节均来自仓库文件本身,如后续仓库升级,请以仓库最新文档与配置为准。
【免费下载链接】seleniumA browser automation framework and ecosystem.项目地址: https://gitcode.com/GitHub_Trending/se/selenium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考