- 测试
【免费下载链接】protractor
E2E test framework for Angular apps
插件(Plugins)是 Protractor 用于扩展基础测试能力的机制:通过在测试执行生命周期的各个钩子(hooks)中插入逻辑,插件可以采集更多运行数据、对测试输出做二次加工,甚至可以改写 Protractor 默认的同步行为。本文以 docs/plugins.md 为骨架,结合仓库中的 lib/plugins.ts、lib/runner.ts、lib/browser.ts 与
spec/plugins/下的真实配置与测试用例,完整讲解如何在配置文件中启用插件、如何从零编写一个符合ProtractorPlugin接口的插件(JavaScript 与 TypeScript 两种写法)、插件的运行生命周期与底层调度实现,并梳理 Protractor 官方及社区插件生态。
Protractor 的插件 API 和官方插件目前均为BETA状态:接口可能在不提升主版本号的情况下发生变化,升级 Protractor 版本时需留意插件兼容性。
一、插件机制概览
Protractor 自身的核心能力是封装 WebDriver 会话、Angular 同步等待(waitForAngular)、元素定位与测试框架调度。插件机制解决的是"测试执行过程中如何优雅地插入自定义逻辑"的问题,典型场景包括:
- 在每个页面加载完成后自动执行可访问性(Accessibility)审计;
- 收集测试执行时间线(timeline)数据并以可视化方式呈现,定位测试延迟瓶颈;
- 在每个测试之后检查浏览器控制台(Console)日志中的警告与错误;
- 在测试运行期间注入自定义断言、向结果对象中追加通过/失败记录;
- 用自定义同步逻辑替换或补充 Protractor 默认的 Angular 稳定性检查。
插件与测试框架(Jasmine、Mocha 等)解耦,只依赖 Protractor 提供的通用钩子,因此同一套插件可以跨框架复用。插件还可以通过返回一个 results 对象来改变 Protractor 的最终输出。
二、在配置文件中启用插件
插件通过 Protractor 配置文件(protractor.conf.js)中的plugins数组启用。数组中的每一项是一个插件配置对象,必须且只能指定三种加载方式中的一种:path、package或inline。
2.1 使用path指向本地插件入口脚本
// protractor.conf.js exports.config = { // ... the rest of your config plugins: [{ // 每个插件唯一必需的字段:插件入口脚本的路径 // 路径相对于配置文件所在目录 path: 'path/to/plugin/index.js', // 插件可以使用这里声明的其他选项,具体见各插件文档 option1: 'foo', option2: 'bar' }] };2.2 使用package引用 npm 安装的插件模块
如果插件以 npm 包形式发布,可以用package选项引用。例如执行npm install example-protractor-plugin之后:
plugins: [{ package: 'example-protractor-plugin', }]2.3 使用inline内联编写小型插件
如果插件很小、只服务于当前这一个配置文件,可以直接将插件对象内联写在配置里:
plugins: [{ inline: { setup: function() { ... }, teardown: function() { ... }, ... } }]2.4 配置解析的底层逻辑
从源码看,lib/plugins.ts 中Plugins构造函数对每个插件配置项按以下优先级解析:
- 若存在
path:调用ConfigParser.resolveFilePatterns(pluginConf.path, true, config.configDir)把相对路径解析为基于配置目录的真实路径;解析失败会抛出'Invalid path to plugin: ' + pluginConf.path; - 否则取
package作为模块路径,通过require(path)加载(因此package既可以填 npm 包名,也可以直接填模块路径); - 否则若存在
inline,直接使用内联的插件对象; - 三者都不存在时抛出错误:
'Plugin configuration did not contain a valid path or inline definition.'。
加载成功后,annotatePluginObj会为插件对象注入运行时属性(详见下文"提供的属性与方法"),并输出调试日志Plugin "<name>" loaded.。
仓库 spec/plugins/smokeConf.js 中的真实示例同时演示了path与inline两种用法;spec/plugins/skipStabilityConf.js 则演示了通过inline定义插件并在setup中操作this(绑定到module.exports的对象)。
三、编写插件:ProtractorPlugin接口
插件本质上是 Node.js 模块,导出(module.exports)一个实现了ProtractorPlugin接口的对象。完整的钩子列表、每个钩子的参数、返回值语义与调用时机,都以 lib/plugins.ts 中ProtractorPlugin接口的 JSDoc 为准。下面逐项说明。
3.1 生命周期钩子(hooks)
| 钩子 | 调用时机 | 说明 |
|---|---|---|
setup() | 测试运行前、WebDriver 会话已建立但测试框架尚未初始化时 | 类似"最早的初始化";可返回 Promise,Protractor 会等待其 resolve |
onPrepare() | 测试框架已初始化、测试用例运行之前 | 与配置文件中的onPrepare对应,可访问框架特有 API(如jasmine.getEnv().addReporter()) |
teardown() | 测试全部运行完、WebDriver 会话销毁之前 | 执行清理工作 |
postResults() | 测试结果已最终确定、相关 job 已更新之后 | 太晚以致无法再向测试结果追加失败断言;失败只会输出到控制台 |
postTest(passed, testInfo) | 每个测试块(Jasmine 中即每个it块)结束后 | 异步返回值不会阻塞下一个测试的执行,但 Protractor 会在输出结果前等待 |
onPageLoad(browser) | browser.get()内部、页面加载完成后、Angular 启动之前 | 无论 Angular 是否启用都会调用 |
onPageStable(browser) | browser.get()内部、Angular 完成 bootstrap/同步之后 | 若browser.waitForAngularEnabled()为false,则不会被调用 |
waitForPromise(browser) | 每个 WebDriver 动作之间,waitForAngular()内部 | 返回自定义 Promise,让 Protractor 额外等待它 resolve |
waitForCondition(browser) | 每个 WebDriver 动作之间,waitForAngular()内部 | 返回布尔值/Promise<boolean>,Protractor 会重复调用直到 truthy |
注意:除onPageLoad外,onPageStable、waitForPromise、waitForCondition都只在waitForAngularEnabled()为true时生效。
所有钩子抛异常或返回 rejected Promise 时,Protractor 都会把这次失败记录为一条失败断言(若结果已上报则输出到控制台),而不会让整个测试进程崩溃——这是插件机制健壮性的核心设计。
3.2 其他属性与函数
name?: string:插件名称,用于结果报告。若不指定,运行时 Protractor 会填入一个合理的默认值(详见下文)。config?: PluginConfig:插件在plugins数组中的那一项配置对象(注意不是整个 Protractor 配置),运行时注入,已有值会被覆盖。skipAngularStability?: boolean:设为true可关闭默认的 Angular 稳定性检查,但保留插件的waitForPromise/waitForCondition同步逻辑(与browser.ignoreSynchronization的"全部关闭"不同)。插件作者常用它来完全接管同步逻辑。addFailure(message?, info?):向测试结果添加一条失败断言。info.specName默认值为插件名 + ' Plugin Tests';可附带stackTrace。结果已上报后再调用会抛错。addSuccess(info?):向测试结果添加一条通过断言。addWarning(message?, info?):向控制台输出警告日志。
3.3 运行时注入机制(Provided properties and functions)
文档中提到"Protractor 加载插件时会为你的module.exports增加额外属性"。对应实现位于 lib/plugins.ts 的annotatePluginObj方法:
- 名称回退链:
obj.name || conf.name || conf.path || conf.package || ('Plugin #' + i); - 注入
obj.config = conf; - 注入
addFailure、addSuccess、addWarning三个方法; - 断言按
specName分组存入this.assertions,失败断言会携带errorMsg与stackTrace。
因此插件内部可以放心使用this.config读取自己的配置项、使用this.addSuccess()等注入方法——它们都是 Protractor 在加载时替你绑定好的。
3.4 JavaScript 插件示例
一个最简插件只需要导出一个对象:
// my-plugin/index.js module.exports = { // 生命周期钩子 setup: function() { /* ... */ }, onPrepare: function() { /* ... */ }, teardown: function() { /* ... */ }, postResults: function() { /* ... */ }, postTest: function(passed, testInfo) { /* ... */ }, onPageLoad: function(browser) { /* ... */ }, onPageStable: function(browser) { /* ... */ }, // 同步扩展 waitForPromise: function(browser) { /* return Promise */ }, waitForCondition: function(browser) { /* return boolean | Promise<boolean> */ }, // 属性 name: 'my-plugin', skipAngularStability: false };仓库中的真实插件示例参见 spec/plugins/plugins/basic_plugin.js(setup/onPrepare钩子)、spec/plugins/plugins/async_plugin.js(异步钩子 +addSuccess+ 自定义name)以及 spec/plugins/plugins/post_test_plugin.js(校验postTest的passed与testInfo字段)。
postTest收到的testInfo对象包含name与category等字段(见 spec/plugins/plugins/post_test_plugin.js 的断言),可用于按测试维度记录数据。
四、在 TypeScript 中编写插件
TypeScript 插件有两种写法,最简单的是直接镜像 JavaScript 语法(通过this访问注入的方法):
export function onPageLoad(): void { this.addSuccess({specName: 'Hello, World!'}); };如果需要更强的类型约束,可以显式使用ProtractorPlugin接口(从protractor包导出),并通过declare var module: any支持module.exports赋值:
import {ProtractorPlugin} from 'protractor'; // creating a "var module: any" will allow use of module.exports declare var module: any; let myPlugin: ProtractorPlugin = { addSuccess(info: {specName: string}) { console.log('on success: ' + info.specName); }, onPageLoad() { this.addSuccess({specName: 'Hello, World!'}); } }; module.exports = myPlugin;仓库中完整的 TypeScript 插件示例位于 exampleTypescript/plugins.ts,与配套的 exampleTypescript/conf.js、exampleTypescript/spec.ts 组成可运行的示例工程,可直接参考其tsconfig.json与package.json进行编译配置。
五、插件生命周期与调度:源码级执行时序
要理解插件何时被调用,最直接的依据是 lib/runner.ts 中Runner.run()的九步流程:
- 建立环境:
driverprovider_.setupEnv(); - 创建浏览器并设置全局变量:
createBrowser(plugins)中会把plugins挂到browser_.plugins_,随后setupGlobals_(); - 插件 setup:
await plugins.setup(); - 执行测试:加载框架(jasmine/mocha/custom 等),框架运行期间通过事件触发
postTest:runner监听testPass/testFail事件,分别调用plugins.postTest(true, testInfo)与plugins.postTest(false, testInfo),并把返回的 Promise 存入pluginPostTestPromises数组(见 lib/runner.ts)——这正是"postTest不阻塞下一个测试、但会在输出结果前等待完成"的实现细节;
- 等待所有 postTest Promise 完成:
await Promise.all(pluginPostTestPromises); - 插件 teardown:
await plugins.teardown(); - 合并测试结果:
results = joinTestLogs(results, plugins.getResults()),插件断言(addSuccess/addFailure累积在assertions中)此时并入最终结果并决定testPassed; - 插件最终清理:
await plugins.postResults(); - 退出进程:根据
testPassed计算退出码0/1。
页面级钩子则定义在 lib/browser.ts:
browser.get()中页面加载完成后调用await this.plugins_.onPageLoad(this);即便waitForAngularEnabled()为false也会执行(见 lib/browser.ts);- Angular 完成 bootstrap/同步后再调用
await this.plugins_.onPageStable(this)(见 lib/browser.ts); - 每次
waitForAngular()中,先await this.plugins_.waitForPromise(this),再用driver.wait反复轮询this.plugins_.waitForCondition(this),直到所有插件条件均为 truthy 或超时(超时消息为'Plugins.waitForCondition()',见 lib/browser.ts)。
5.1 调度器的容错设计
lib/plugins.ts 的safeCallPluginFun是所有钩子的统一执行入口:它对每个插件钩子用 async 包装,try/catch捕获同步异常,并处理 rejected Promise——失败时若结果尚未上报则pluginObj.addFailure(...),否则把失败打印为一条"<插件名> Runtime"结果。pluginFunFactory(见 lib/plugins.ts)则把同名钩子批量分发到所有定义了该函数的插件,用Promise.all汇总,因此多个插件可以同时注册同一个钩子。
5.2 结果合并与输出
getResults()(见 lib/plugins.ts)把插件累积的断言按 spec 名组装成{failedCount, specResults},并通过printPluginResults以绿/红颜色输出Pass:/Fail:与失败详情(含堆栈缩进)。这套结构遵循 lib/frameworks/README.md 中描述的框架结果对象要求,因此插件断言能与框架自身的测试结果合并后统一计入退出码。
5.3skipAngularStability的运行时判定
Plugins.skipAngularStability()(见 lib/plugins.ts)用some()检测任一已加载插件是否开启该标志,供同步逻辑在运行时查询。仓库测试 spec/plugins/skipStabilityConf.js 与 spec/plugins/specs/skip_stability_spec.js 演示了如何在setup中暴露一个全局函数(protractor._PluginSetSkipStability)来在测试运行中途切换该标志,从而在同一套测试里验证"关闭同步时不等待$timeout(状态停在pending...)"与"开启同步时等待(状态变为done)"两种行为。
六、官方(First Party)插件
Protractor 团队维护的官方插件以独立 npm 包 + GitHub 仓库形式发布(本仓库内不包含其源码):
- Accessibility Plugin(可访问性插件):
protractor-accessibility-plugin。对 Web 应用运行一组可访问性审计,把审计结果转化为测试结果。 - Timeline Plugin(时间线插件):
protractor-timeline-plugin。从多个来源收集测试时间线信息并以可视化方式呈现,帮助定位测试中的延迟问题。 - Console Plugin(控制台插件,仅 Chrome):
protractor-console-plugin。在每个测试之后检查浏览器日志中的警告与错误。 - ngHint Plugin(已停止维护):
protractor-ng-hint-plugin。基于 Angular Hint 生成运行时提示并把提示转化为 Protractor 测试。
官方插件的安装方式即为普通 npm 安装后通过package字段引用:
npm install protractor-accessibility-pluginplugins: [{ package: 'protractor-accessibility-plugin' }]七、社区插件(Community Plugins)
社区插件不是 Protractor 团队开发或维护的,遇到问题应反馈给对应插件的开发者。文档收录的插件包括:
- Protractor testability plugin(
protractor-testability-plugin):为"不是基于 AngularJS 服务开发的"功能提供同步测试支持。例如使用 WebSocket 与服务器通信、或使用非 AngularJS 框架构建的 Web 应用,借助该插件可避免在测试中额外编写等待代码。 - protractor-fail-fast:允许 Protractor "快速失败"——一旦某个测试运行器遇到失败测试,就强制所有测试运行器退出。在 CI 等"失败即构建失败"的场景中可大幅节省时间。
- protractor-numerator:提供可读性更好的取元素函数,例如
.second()、.third(),替代.get(1)、.get(2)。 - Ng-apimock:为 AngularJS 与 Angular 应用增加基于场景(scenario)的 API mock 能力,可用于本地开发与 Protractor 测试。
- protractor-cucumber-steps:提供基于 Protractor 编写的 Cucumber step definitions,用于端到端测试。
八、最佳实践小结
- 每个插件配置只能指定
path、package、inline三者之一,path相对配置文件目录,package用于 npm 模块。 - 优先使用官方定义的钩子而不是侵入测试框架:钩子与框架解耦,且自带"失败不崩溃"的容错(失败会变成断言记录)。
- 善用
this.config与注入方法:config是你自己的配置块,addSuccess/addFailure/addWarning用于产出结果;specName不传时默认是插件名 + ' Plugin Tests'。 - 异步钩子返回 Promise:Protractor 会等待
setup、teardown等钩子的 Promise;postTest的 Promise 不阻塞下一个测试但会在结果输出前等待。 - 需要接管同步时使用
skipAngularStability:它只关闭 Angular 稳定性检查,保留插件自身的waitForPromise/waitForCondition,与全局关闭的ignoreSynchronization不同。 - TypeScript 插件:可直接镜像 JS 写法,也可用
ProtractorPlugin接口获得完整类型提示,参考 exampleTypescript/plugins.ts。 - 关注 BETA 状态:插件 API 可能在小版本中变化,升级 Protractor 后建议回归验证插件行为。
九、进一步阅读
- 插件接口完整定义与 JSDoc:lib/plugins.ts
- 插件调度与测试生命周期:lib/runner.ts(重点看
run()与runTestPreparer()) - 页面级钩子调用点:lib/browser.ts(
get()、waitForAngular()) - 插件真实测试配置与用例:
spec/plugins/目录下的smokeConf.js、waitForAngularConf.js、skipStabilityConf.js及对应specs/用例 - TypeScript 插件示例:exampleTypescript/plugins.ts
- 插件结果对象与框架对接约定:lib/frameworks/README.md
- 测试
【免费下载链接】protractor
E2E test framework for Angular apps
相关推荐
Egg 插件机制完全指南:从配置使用到源码级原理剖析
Egg 插件机制完全指南:从配置使用到源码级原理剖析 本文以 Egg(本仓库 egg https://link.gitcode.com/i/3790aa7d9f
后端Web框架Datasette 插件系统完全指南:从安装配置到源码级原理
Datasette 插件系统完全指南:从安装配置到源码级原理 本指南以 Datasette 官方文档 docs/plugins.rst https://link
数据可视化数据分析数据库后端Docusaurus插件系统深度解析:从基础使用到高级配置
Docusaurus插件系统深度解析:从基础使用到高级配置 前言 Docusaurus作为一个现代化的文档网站构建工具,其核心设计理念之一就是"插件化架构"。本
静态站点文档前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考