分享

phantomJs之殇,chrome

 quasiceo 2018-01-17
投递人 itwriter 发布于 2018-01-17 09:59 评论(0) 有99人阅读 原文链接 [收藏] « »

  齐磊

  ThoughtWorks 资深质量保证咨询师,超过 7 年的软件开发与测试经验,擅长测试开发,软件开发流程的自动化,目前致力于开阔移动端测试开发领域。

  自 2017 年中以来,Chrome 用户可以选择以 headless 模式运行浏览器。此功能非常适合运行前端浏览器测试,而无需在屏幕上显示操作过程。在此之前,这主要是 PhantomJS 的领地,但 Headless Chrome 正在迅速取代这个由 JavaScript 驱动的 WebKit 方法。Headless Chrome 浏览器的测试运行速度要快得多,而且行为上更像一个真正的浏览器,虽然我们的团队发现它比 PhantomJS 使用更多的内存。有了这些优势,用于前端测试的 Headless Chrome 很可能成为事实上的标准。

  随着 Google 在 Chrome 59 版本放出了 headless 模式,Ariya Hidayat 决定放弃对 Phantom.js 的维护,这也标示着 Phantom.js 统治 fully functional headless browser 的时代将被 chrome-headless 代替。

  Headless Browser

  也许很多人对无头浏览器还是很陌生,我们先来看看维基百科的解释:

A headless browser is a web browser without a graphical user interface.

Headless browsers provide automated control of a web page in an environment similar to popular web browsers, but are executed via a command-line interface or using network communication.

  对,就是没有页面的浏览器。多用于测试 web、截图、图像对比、测试前端代码、爬虫(虽然很慢)、监控网站性能等。

  为什么要使用 headless 测试?

  headless broswer 可以给测试带来显著好处:

  1. 对于 UI 自动化测试,少了真实浏览器加载 css,js 以及渲染页面的工作。无头测试要比真实浏览器快的多。
  2. 可以在无界面的服务器或 CI 上运行测试,减少了外界的干扰,使自动化测试更稳定。
  3. 在一台机器上可以模拟运行多个无头浏览器,方便进行并发测试。

  headless browser 有什么缺陷?

  以 phantomjs 为例

  1. 虽然 Phantom.js 是 fully functional headless browser,但是它和真正的浏览器还是有很大的差别,并不能完全模拟真实的用户操作。很多时候,我们在 Phantom.js 发现一些问题,但是调试了半天发现是 Phantom.js 自己的问题。
  2. 将近 2k 的 issue,仍然需要人去修复。
  3. Javascript 天生单线程的弱点,需要用异步方式来模拟多线程,随之而来的 callback 地狱,对于新手而言非常痛苦,不过随着 es6 的广泛应用,我们可以用 promise 来解决多重嵌套回调函数的问题。
  4. 虽然 webdriver 支持 htmlunit 与 phantomjs,但由于没有任何界面,当我们需要进行调试或复现问题时,就非常麻烦。

  那么 Headless Chrome 与上面提到 fully functional headless browser 又有什么不同呢?

  什么是 Headless Chrome?

  Headless Chrome 是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有 Chrome 支持的特性,在命令行中运行你的脚本。相比于其他浏览器,Headless Chrome 能够更加便捷的运行 web 自动化测试、编写爬虫、截取图等功能。

  有的人肯定会问:看起来它的作用和 phantomjs 没什么具体的差别?

  对,是的,Headless Chrome 发布就是来代替 phantomjs。

  我们凭什么换用 Headless Chrome?

  1. 我爸是 Google,那么就意味不会出现 phantomjs 近 2k 问题没人维护的尴尬局面。 比 phantomjs 有更快更好的性能。
  2. 有人已经做过实验,同一任务,Headless Chrome 要比现 phantomjs 更加快速的完成任务,且占用内存更少。https:///benchmark-headless-chrome-vs-phantomjs-e7f44c6956c
  3. chrome 对 ECMAScript 2017 (ES8) 支持,同样 headless 随着 chrome 更新,意味着我们也可以使用最新的 js 语法来编写的脚本,例如 async,await 等。
  4. 完全真实的浏览器操作,chrome headless 支持所有 chrome 特性。
  5. 更加便利的调试,我们只需要在命令行中加入–remote-debugging-port=9222,再打开浏览器输入 localhost:9222(ip 为实际运行命令的 ip 地址)就能进入调试界面。

  能带给 QA 以及项目什么好处?

  前端测试改进

  以目前的项目来说,之前的前端单元测试以及组件测试是用 karma 在 phantomjs 运行的,非常不稳定,在远端 CI 上运行时经常会莫名其妙的挂掉,也找不出来具体的原因,自从 Headless Chrome 推出后,我们将 phantomjs 切换成 Headless Chrome,再也没有出现过异常情况,切换也非常简单,只需要把 karma.conf.js 文件中的配置改下就 OK 了。如下

customLaunchers: { myChrome: { base: 'ChromeHeadless', flags: ['--no-sandbox', '--disable-gpu', '--remote-debugging-port=9222'] } },

browsers: ['myChrome'],

  UI 功能测试改进

  原因一,Chrome-headless 能够完全像真实浏览器一样完成用户所有操作,再也不用担心跑测试时,浏览器受到干扰,造成测试失败

  原因二,之前如果我们像要在 CI 上运行 UI 自动化测试,非常麻烦。必须使用 Xvfb 帮助才能在无界面的 Linux 上运行 UI 自动化测试。(Xvfb 是一个实现了 X11 显示服务协议的显示服务器。 不同于其他显示服务器,Xvfb 在内存中执行所有的图形操作,不需要借助任何显示设备。)现在也只需要在 webdriver 启动时,设置一下 chrome option 即可,以 capybara 为例:

  Capybara.register_driver :selenium_chrome do app

  Capybara::Selenium::Driver.new (app, browser: :chrome,

desired_capabilities: {

"chromeOptions" => {

"args" => [ "--incognito",

"--allow-running-insecure-content",

"--headless",

"--disable-gpu"

]}

})

  end

  无缝切换,只需更改下配置,就可以提高运行速度与稳定性,何乐而不为。

  Google 终极大招

  Google 最近放出了终极大招——Puppeteer(Puppeteer is a Node library which provides a high-level API to control headless Chrome over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome.)

  类似于 webdriver 的高级别的 api,去帮助我们通过 DevTools 协议控制无界面 Chrome。

  在 puppteteer 之前,我们要控制 chrome headless 需要使用 chrome-remote-interface 来实现,但是它比 Puppeteer API 更接近低层次实现,无论是阅读还是编写都要比 puppteteer 更复杂。也没有具体的 dom 操作,尤其是我们要模拟一下 click 事件,input 事件等,就显得力不从心了。

  我们用同样 2 段代码来对比一下 2 个库的区别。

  首先来看看 chrome-remote-interface

  const chromeLauncher = require ('chrome-launcher');

  const CDP = require ('chrome-remote-interface');

  const fs = require ('fs');

  function launchChrome (headless=true) {

  return chromeLauncher.launch ({

  // port: 9222, // Uncomment to force a specific port of your choice.

  chromeFlags: [

  '--window-size=412,732',

  '--disable-gpu',

headless ? '--headless' : ''

  ]

  });

  }

  (async function () {

const chrome = await launchChrome ();

const protocol = await CDP ({port: chrome.port});

const {Page, Runtime} = protocol;

await Promise.all ([Page.enable (), Runtime.enable ()]);

Page.navigate ({url: 'https://www.github.com/'});

await Page.loadEventFired (

console.log ("start")

);

const {data} = await Page.captureScreenshot ();

fs.writeFileSync ('example.png', Buffer.from (data, 'base64'));

// Wait for window.onload before doing stuff.

protocol.close ();

chrome.kill (); // Kill Chrome.

  再来看看 puppeteer

const puppeteer = require ('puppeteer');

(async () => {

const browser = await puppeteer.launch ();

const page = await browser.newPage ();

await page.goto ('https://www.github.com');

await page.screenshot ({path: 'example.png'});

await browser.close ();

})();

  对,就是这么简短明了,更接近自然语言。没有 callback,几行代码就能搞定我们所需的一切。

  总结

  目前 Headless Chrome 仍然存在一些问题,还需要不断完善,我们应该拥抱变化,适应它,让它给我们的工作带来更多帮助。

1
0
来自: insights.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多