WebdriverIO 示例仓库实战指南:从云服务、多浏览器到自定义扩展的全套官方示例
WebdriverIO 示例仓库实战指南:从云服务、多浏览器到自定义扩展的全套官方示例
WebdriverIO 的 examples 目录是官方维护的"教学型"示例集合,覆盖了云测试平台接入、多浏览器(Multiremote)、Page Object 模式、WDIO 测试运行器三大框架、自定义 Reporter/Service,以及 Appium、BiDi 和 DevTools 协议等进阶玩法。本文以该目录为骨架,逐条拆解每个示例的启动方式、配置要点与源码级实现细节,帮助你把这些官方示例直接移植到自己的项目里。
整体结构与快速起步
examples 目录按主题划分为多个子目录,每个子目录都是独立的最小可运行工程,自带 package.json 与配置:
| 目录 | 主题 | 运行方式 |
|---|---|---|
| cloudservices | Sauce Labs / BrowserStack / TestingBot / Kobiton 云平台 | node xxx.js |
| appium | iOS WebView 手势与 touchAction | node appium-draws-its-logo.js |
| bidi | WebDriver BiDi 日志与预加载脚本 | node logging.js / node scripting.js |
| pageobject | Page Object 模式 + WDIO Testrunner | npm test |
| wdio | Mocha / Jasmine / Cucumber / 自定义 Reporter / Multiremote / Vite 组件测试 | npm run test:* |
| standalone | remote() 独立脚本(Headless) |
node sample.js |
| devtools | Puppeteer 与 WebDriver 命令混用 | node intercept.js |
由于示例目录本身不携带任何 dev-dependencies(这一点在 examples/README.md 中有明确说明),最省事的做法是先克隆 WebdriverIO 仓库并构建整个 monorepo:
git clone git@github.com:webdriverio/webdriverio.git
cd ./webdriverio
pnpm install
pnpm run setup
完成依赖安装后,每个示例即可按各自 README 的命令直接运行。也可以在真实项目中用 WebdriverIO 官方 Boilerplate 模板初始化出完整的 dev-dependencies,再把示例文件拷贝进去运行。
云测试平台:一套 remote() 多路接入
cloudservices 用四个文件演示同一个测试脚本如何无缝切换到 Sauce Labs、BrowserStack、TestingBot 与 Kobiton。其 package.json 只声明了一个依赖 webdriverio,脚本均为 node xxx.js 直跑,典型用法:
cd ./examples/cloudservices
export SAUCE_USERNAME="username"
export SAUCE_ACCESS_KEY="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
npm run test:saucelabs
四个平台的凭据环境变量与命令如下:
| 平台 | 必需环境变量 | 启动命令 |
|---|---|---|
| Sauce Labs | SAUCE_USERNAME / SAUCE_ACCESS_KEY |
npm run test:saucelabs |
| BrowserStack | BROWSERSTACK_USERNAME / BROWSERSTACK_ACCESS_KEY |
npm run test:browserstack |
| TestingBot | TESTINGBOT_KEY / TESTINGBOT_SECRET |
npm run test:testingbot |
| Kobiton | KOBITON_USERNAME / KOBITON_ACCESS_KEY |
npm run test:kobiton |
所有脚本都通过 import { remote } from 'webdriverio' 创建浏览器实例,核心差别集中在 remote() 的配置对象上。以 saucelabs.js 为例:
const browser = await remote({
capabilities: {
browserName: 'chrome',
version: 'latest',
platform: 'Windows 10',
tags: ['examples'],
name: 'This is an example test',
// Open Sauce 免费账户下必须置为 true,否则任务状态只显示
// "Finished" 而不会标记 passed/failed;商用私有账户可省略
'public': true
},
user: process.env.SAUCE_USERNAME,
key: process.env.SAUCE_ACCESS_KEY,
logLevel: 'trace'
})
BrowserStack 的差异点在于使用其专有的 capabilities(browser / browser_version / os / os_version / resolution),并显式指定 hub 地址与端口:
await remote({
capabilities: {
browser: 'Chrome',
browser_version: '46.0',
os: 'Windows',
os_version: '10',
resolution: '1024x768'
},
hostname: 'hub.browserstack.com',
port: 80,
user: process.env.BROWSERSTACK_USERNAME,
key: process.env.BROWSERSTACK_ACCESS_KEY
})
TestingBot 同样是 hub.testingbot.com:80;而 Kobiton 面向移动端,走 HTTPS(protocol: 'https'、port: 443、hostname: 'api.kobiton.com'),capabilities 也换成了 deviceName、platformName、platformVersion、deviceGroup: 'KOBITON' 等移动语义字段,并用 sessionName / sessionDescription / deviceOrientation / captureScreenshots 描述会话。
四个脚本的测试体完全相同:打开 https://webdriver.io,通过 browser.$('.ds-input') 定位搜索框并 addValue('click'),点击 .algolia-docsearch-suggestion--title,最后 getTitle() 并在控制台打印标题、deleteSession() 收尾。这说明 WebdriverIO 的 remote() API 与平台解耦——切换云服务商只需改配置,测试代码零改动。
Multiremote:一个浏览器实例管理多个会话
Multiremote 允许在单个测试进程内同时驱动多个浏览器,专门用于测试聊天室、视频通话这类需要多端交互的场景,而不是做并行提速。运行时通过 browserA / browserB 这样带后缀的实例访问各自的浏览器。
wdio/multiremote/wdio.conf.js 展示了 Testrunner 版的多浏览器能力:capabilities 不再是数组,而是一个键值对象,每个键对应一个浏览器会话:
capabilities: {
browserA: {
capabilities: { browserName: 'chrome' }
},
browserB: {
capabilities: { browserName: 'chrome' }
}
}
对应测试 mocha.test.js 演示了一个真实的聊天场景:browserA 发送消息,browserB 读取并回复——这正是 README 中 "不是并行而是交互" 的直观体现:
it('should login the browser B', async () => {
const nameInput = await browserB.$('.usernameInput')
await nameInput.addValue('Browser B')
await browserB.keys('Enter')
})
运行方式:
cd ./examples/wdio
npm run test:multiremote
独立脚本与 WebDriver 命令示例
standalone:无 Testrunner 的最小脚本
standalone/sample.js 展示脱离测试运行器的裸脚本写法,并演示了 DevTools 协议下的 headless 启动:
const browser = await remote({
capabilities: {
browserName: 'chrome',
'wdio:devtoolsOptions': { headless: true }
}
})
脚本在 try/catch 中执行 url() / getUrl(),无论成功失败都会调用 deleteSession() 释放会话——这是所有独立脚本都要养成的收尾习惯。
devtools:WebDriver 与 Puppeteer 混编
devtools/intercept.js 演示了"先用 WebDriver 打开页面,再切换到 Puppeteer 拦截请求"的混合模式:通过 browser.getPuppeteer() 拿到 Puppeteer 浏览器,page.setRequestInterception(true) 后将 .png 请求改写为另一张图片,再切回 browser.refresh() 继续使用 WebDriver 命令。它展示了 WebdriverIO 在 DevTools 协议下与 Puppeteer 生态互通的能力。
appium:移动手势驱动的画图示例
appium/appium-draws-its-logo.js 面向 iOS WebView:remote() 指向本地 Appium 端口 4723,capabilities 使用 platformName / deviceName / app 等移动配置。随后用 driver.touchAction() 组合 press / moveTo / release 完成滑动,并调用 helpers/drawHelper.js 导出的 circleAction / arcAction / innerArcAction 画出一个圆与三段内弧组成的 WebdriverIO Logo。注意该示例需要旧的 v4 仓库编译出的 webview 应用作为被测对象。
bidi:WebDriver BiDi 事件订阅
WebDriver BiDi 通过 WebSocket 提供双向实时事件流。bidi/logging.js 演示了订阅与监听两个事件:先 sessionSubscribe({ events: <a href="https://link.gitcode.com/i/8099742d8e7f898d2cdbe4a16ad545fd" target="_blank">'log.entryAdded'] }) 并注册 browser.on('log.entryAdded', ...) 实时打印控制台日志;再订阅 network.responseCompleted 输出每个网络响应;[bidi/scripting.js 则通过 scriptAddPreloadScript() 注册预加载脚本,让 window.bar='foo' 在页面加载前注入,再用 browser.execute() 读取。两个文件均在 capabilities 中开启 webSocketUrl: true,这是 WebdriverIO 自动建立 BiDi WebSocket 通道的前提。若需完整代码可直接参考 bidi/README.md。
Page Object 模式:面向对象封装的官方示例
pageobject 演示了一个用 JavaScript 类 + Object.create 风格继承搭建的 Page Object 测试套件。核心思想是:把页面专属的选择器与操作抽象进 Page Object,测试只关心业务行为,这样即使页面彻底改版,也只需集中修改 Page Object 一处。
父类 pageobjects/page.js 只提供一个所有页面通用的 open(path)(内部委托给 browser.url)。子类 form.page.js 通过 getter 声明元素、重写页面方法:
class FormPage extends Page {
get username () { return $('#username') }
get password () { return $('#password') }
get submitButton () { return $('#login button[type=submit]') }
get flash () { return $('#flash') }
open () {
return super.open('login')
}
submit () {
return this.submitButton.click()
}
}
export default new FormPage()
测试 specs/form.spec.js 因此变得非常干净——完全不出现选择器:
describe('auth form', () => {
it('should deny access with wrong creds', async () => {
await FormPage.open()
await FormPage.username.addValue('foo')
await FormPage.password.addValue('bar')
await FormPage.submit()
await expect(FormPage.flash).toHaveText(
expect.stringContaining('Your username is invalid!')
)
})
})
运行该示例:
cd ./examples/pageobject
npm test
其 package.json 将 test 脚本定义为 wdio run ./wdio.conf.js,依赖仅 @wdio/cli、@wdio/spec-reporter、@wdio/local-runner、@wdio/mocha-framework 四个包。wdio.conf.js 是一份信息量很大的参考配置:specs 使用通配 *.spec.js;baseUrl 指向 http://the-internet.herokuapp.com(被测站是 the-internet 的登录页);waitforTimeout: 150000、connectionRetryTimeout: 120000、connectionRetryCount: 3 定义了超时与重试策略;framework: 'mocha'、reporters: ['spec'],mochaOpts 里 ui: 'bdd'、timeout: 30000,并在文件后半段注释了从 onPrepare 到 afterFeature 的完整钩子签名——是学习 WDIO 配置的最佳模板之一。
WDIO Testrunner 全家桶:多框架、自定义扩展与组件测试
wdio 目录集中展示了 Testrunner 的各种用法,其 package.json 以 workspace:* 形式引用了仓库内的框架适配器与 reporter 包。全部命令在目录下执行:
cd ./examples/wdio
npm run test:mocha
npm run test:jasmine
npm run test:cucumber
npm run test:multiremote
npm run test:customReporter
npm run test:viteVue
三种框架适配
- Mocha:mocha/wdio.conf.ts 是带完整类型标注(
WebdriverIO.Config)的 TS 配置,specs指向./mocha.test.ts,framework: 'mocha',reporter 同时启用了spec、dot、junit三个,并用onPrepare/onComplete钩子输出生命周期日志。 - Jasmine:jasmine/ 下为
jasmine.spec.ts+wdio.conf.ts,把framework换成'jasmine'即可。 - Cucumber:cucumber/wdio.conf.ts 中
specs指向features/my-feature.feature,framework: 'cucumber',并通过cucumberOpts.require加载 TypeScript 编写的 step-definitions.ts,实现 BDD 风格测试。
自定义 Reporter
custom-reporter 演示如何编写自己的 Reporter。核心类 my.custom.reporter.js 继承 @wdio/reporter 的 WDIOReporter,在构造函数中用 this.write(...) 输出日志,并覆盖 onRunnerStart、onBeforeCommand、onAfterCommand、onSuiteStart、onHookStart/End、onTestStart、onTestPass/Fail/Retry/Skip/End、onSuiteEnd、onRunnerEnd 等一整套生命周期钩子。在 wdio.conf.js 中以"类 + 选项对象"的元组形式注册:
reporters: [[CustomReporter, {
someOption: 'foobar'
}]]
(examples/wdio/package.json 中还定义了 test:customService,对应 custom-service 目录中基于 before / after 钩子的自定义 Service 示例,原理与 Reporter 一致:挂钩进测试进程的生命周期。)配套的 mocha.test.js 是一个极简 Spec,用于观察各钩子的触发顺序。
Vite + Vue 组件测试
vite-vue-example 把 WebdriverIO 浏览器运行器与 Vite 集成,测试 Vue 3 组件。HelloWorld.test.ts 配合 wdio.conf.ts 即可在真实浏览器中挂载组件并断言交互行为;命令会先 cd ./vite-vue-example && npm run wdio,使用 @wdio/browser-runner 完成编译与测试。
小结
examples/ 目录的价值在于它把 WebdriverIO 最常用的能力浓缩成了可一键运行的最小示例:云平台接入只需替换 remote() 的 user/key 与 capabilities;Multiremote 用一个配置对象同时驱动多个浏览器解决多端交互测试;Page Object 与 WDIO Testrunner 各框架示例可以直接当配置模板抄;自定义 Reporter/Service 与 BiDi 示例则展示了协议层与插件体系的可扩展面。配合每个子目录的 README 与 package.json,这些示例是理解并上手 WebdriverIO 最直接的入口。