Thanks to visit codestin.com
Credit goes to github.com

Skip to content

UI测试:从WebdriverIO and Selenium 转 Puppeteer (译) #49

@aermin

Description

@aermin

我的动机

我在GoDaddy的一个全栈团队工作,帮助支持一系列产品:从GoDaddy的新客户门户(网站)到用于开发新Web内容的内部工具。
为了测试这些当中的每一个前端(译者注:界面和功能),我们使用了带有Selenium (译者注:Selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样) 浏览器自动化的测试框架:WebdriverIO。虽然我们对setup的跨平台可配置性表示赞赏,但我们遇到了关于Selenium的一些问题。

部署浏览器Images

将Selenium浏览器整合到部署中时出现了第一个问题。我们尝试的一种方法是在部署时将Selenium浏览器下载到与代码库相同的容器中。这导致了复杂的Dockerfiles,当浏览器部署出现问题时,调试成本很高。

幸运的是,这个问题有一个简单的解决方案:Selenium预先构建的Docker容器。这些images是模块化的,维护的,随时可用的。但它带来了自己的问题。默认情况下,Docker不允许您在本地开发时查看浏览器UI,但images带有方便的调试模式以解决此问题。

CICD Flakiness

第二个痛点是没有我们的CICD管道,Selenium将导致薄脆度。(The second pain point was that Selenium was causing flakiness within our CICD pipeline. 不大好翻译....) 。 Jenkins slaves 未能连接到他们刚刚启动的浏览器容器。在某些时候,4个版本中有3个会失败。

当然,仅仅责备Selenium会有点不公平。鉴于我们的开发和CICD设置,它只是证明是一个难以维护的系统。如果我们有专门的时间让我们的管道更能适应这些故障(鉴于故障只发生在我们的CICD管道中,这(时间成本)本身就很昂贵),我们几乎可以肯定地减少了薄脆度。然而,测试套件和浏览器之间的松散耦合是一个持久的失败点,并且把时间花在尝试解决我们的CICD问题上可能是一个更好的花费时间的选择… 好吧,找另一个UI测试框架去。

来到Puppeteer

正是在这一点上,我们发现了Puppeteer,一个由Google开发的detached-head(独立头部)的UI测试框架。Puppeteer实现了Chrome devtools协议,目前只支持Google Chrome和Chromium。缺乏跨浏览器支持是一个问题,但它也允许测试框架和浏览器之间更紧密的耦合,消除了我们与WebdriverIO / Selenium的主要摩擦点。

方便的是,我们最积极开发的代码库是一个内部工具,所以Puppeteer只支持一个浏览器是可以接受的。这似乎是使用Puppeteer的绝佳机会。而Puppeteer对提高可靠性的允诺太诱人了。

那么这两个框架之间的过渡到底是什么样的呢?让我们从一个最显眼的比较点开始吧......

语法

从语法上讲,Puppeteer和WebdriverIO看起来非常相似。举例来说,每个框架的代码用于单击className 是 myLinkComponent的链接:

WebdriverIO:

await browser.waitForExist('.myLinkComponent');
await browser.click('.myLinkComponent');

Puppeteer:

await page.waitFor('.myLinkComponent');
await page.click('.myLinkComponent');

这些示例中最显着的差异(这实际上与它们的相似之处)可能是WebdriverIO使用全局浏览器常量,而Puppeteer使用在测试开始时创建的页面对象。

再举一个例子,考虑使用className 为 myTextComponent的Puppeteer和WebdriverIO代码去查找和读取文本组件:

WebdriverIO:

await browser.waitForExist('.myTextComponent');
const myText = await browser.getText('.myTextComponent');

Puppeteer:

await page.waitFor('.myTextComponent');
const myText = await page.$eval('.myTextComponent', component => component.textContent);

这里的Puppeteer示例有点不那么直截了当:没有用于抓取文本内容的实用程序,所以我们必须传递一个函数来将适当的内容写入页面的$ eval方法。不过,测试代码非常相似。

两种语法之间最重要的区别可能是您如何配置测试。大多数WebdriverIO的配置都发生在wdio.conf.js文件中(示例):

同时,Puppeteer就像一个普通的npm模块,你可以在运行它们的代码中配置测试(更简洁):

const browser = await puppeteer.launch({
  headless: false
});
const page = await browser.newPage();

这种区别突出了两种语言之间的差异:WebdriverIO允许更多种类的配置,其中Puppeteer花费更少的精力(开箱即用)。

通常,两个框架之间的语法相似性使我们可以轻松地将现有的测试套件从WebdriverIO移植到Puppeteer。

开发周期

我已经明确了我的团队对于Selenium的痛点(不好用的地方),所以我会使用我喜欢的Puppeteer。

默认情况下,Puppeteer在headless模式下运行,这意味着测试执行时不会实际打开Chrome UI窗口。对于本地开发,您通常需要禁用headless模式,以便可以在浏览器中观察测试执行情况。

Puppeteer支持许多选项,使本地开发更容易。这里有些例子:

const browser = await puppeteer.launch({
  headless: false,  // Turn on local browser UI
  devtools: true,  // Open Chrome devtools at the beginning of the test
  slowMo: 250  // Wait 250 ms between each step of execution
});
const page = await browser.newPage();

// Log browser output to console
page.on('console', (msg) => {
  console.log('console:log', ...msg.args);
})

// Handle dialogs.
page.on('dialog', (dialog) => {
  if (dialog.type() === 'alert') {
    await dialog.dismiss();
  }
});

// Take a screenshot of the page.
await page.screenshot({path: 'my-screenshot.png'});

这篇博文可以让您更详细地了解这些和其他的Puppeteer开发选项。

最重要的是,在我们采用Puppeteer后的五个月里,框架本身并没有在我们的CICD管道中造成任何瑕疵(caused no flakiness)。这是一个框架,开发人员可以确信本地传递的测试也会传递给CICD。

结论

WebdriverIO/Selenium:

  • 与浏览器的松散的耦合提供了频繁的故障点
  • 平台不可知
  • 他们网站上有大量的示例和文档

Puppeteer:

  • 没有跨平台测试
  • 更可靠的自动化测试
  • 随时可以自定义调试

简而言之,有许多应用需要支持的浏览器不仅仅是谷歌浏览器。在这些情况下,Puppeteer不是一个选择,您应该使用WebdriverIO && Selenium 或其他跨平台UI测试框架。否则,考虑Puppeteer。转换很简单,根据您团队的开发周期和CICD设置,它可能是一个对开发者更加友好且可靠的UI测试体验。

资源:

原文

UI Testing: moving from WebdriverIO and Selenium to Puppeteer

译者附带:

image

npm trends :puppeteer vs selenium vs webdriverio

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions