☰
Protractor 插件体系深度指南:从配置使用到源码级原理解析
2026/9/25 10:48:11 网站建设 项目流程
  • 测试

【免费下载链接】protractor

E2E test framework for Angular apps

项目地址:https://gitcode.com/gh_mirrors/pr/protractor
点击查看免费下载

插件(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构造函数对每个插件配置项按以下优先级解析:

  1. 若存在path:调用ConfigParser.resolveFilePatterns(pluginConf.path, true, config.configDir)把相对路径解析为基于配置目录的真实路径;解析失败会抛出'Invalid path to plugin: ' + pluginConf.path;
  2. 否则取package作为模块路径,通过require(path)加载(因此package既可以填 npm 包名,也可以直接填模块路径);
  3. 否则若存在inline,直接使用内联的插件对象;
  4. 三者都不存在时抛出错误:'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()的九步流程:

  1. 建立环境:driverprovider_.setupEnv();
  2. 创建浏览器并设置全局变量:createBrowser(plugins)中会把plugins挂到browser_.plugins_,随后setupGlobals_();
  3. 插件 setup:await plugins.setup();
  4. 执行测试:加载框架(jasmine/mocha/custom 等),框架运行期间通过事件触发postTest:
    • runner监听testPass/testFail事件,分别调用plugins.postTest(true, testInfo)与plugins.postTest(false, testInfo),并把返回的 Promise 存入pluginPostTestPromises数组(见 lib/runner.ts)——这正是"postTest不阻塞下一个测试、但会在输出结果前等待完成"的实现细节;
  5. 等待所有 postTest Promise 完成:await Promise.all(pluginPostTestPromises);
  6. 插件 teardown:await plugins.teardown();
  7. 合并测试结果:results = joinTestLogs(results, plugins.getResults()),插件断言(addSuccess/addFailure累积在assertions中)此时并入最终结果并决定testPassed;
  8. 插件最终清理:await plugins.postResults();
  9. 退出进程:根据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-plugin
plugins: [{ 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,用于端到端测试。

八、最佳实践小结

  1. 每个插件配置只能指定path、package、inline三者之一,path相对配置文件目录,package用于 npm 模块。
  2. 优先使用官方定义的钩子而不是侵入测试框架:钩子与框架解耦,且自带"失败不崩溃"的容错(失败会变成断言记录)。
  3. 善用this.config与注入方法:config是你自己的配置块,addSuccess/addFailure/addWarning用于产出结果;specName不传时默认是插件名 + ' Plugin Tests'。
  4. 异步钩子返回 Promise:Protractor 会等待setup、teardown等钩子的 Promise;postTest的 Promise 不阻塞下一个测试但会在结果输出前等待。
  5. 需要接管同步时使用skipAngularStability:它只关闭 Angular 稳定性检查,保留插件自身的waitForPromise/waitForCondition,与全局关闭的ignoreSynchronization不同。
  6. TypeScript 插件:可直接镜像 JS 写法,也可用ProtractorPlugin接口获得完整类型提示,参考 exampleTypescript/plugins.ts。
  7. 关注 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

项目地址:https://gitcode.com/gh_mirrors/pr/protractor
点击查看免费下载
上一篇:Windows热键侦探:揭秘键盘快捷键冲突的神秘面纱
下一篇:你的Mac也能畅玩iOS游戏吗?PlayCover打破苹果生态壁垒

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询