Using Watir to run Selenium with Headless Chrome

Michal Peled
MyHeritage Engineering
5 min readJul 31, 2018

With a suite of more than 1,000 tests and counting, that run multiple times a day in Chrome, the introduction of the Chrome Headless mode brought high hopes for significant run time reduction. In this article we’ll discuss what Headless mode is, how it is integrated into Watir, and why it is not the dramatic game changer we had hoped it would be.

Watir is an open source Ruby library for automating tests. It is powered by Selenium, interacting with browsers in the same way people do: launching the browser, browsing through different pages, clicking links, filling out forms, etc.

Because most automated tests carried out this way are done without an actual person viewing them — even running on multiple browser instances at once — the visual rendering of the pages is redundant. Therefore, servers with no screen attached to them are often used for this purpose. However, running these tests without screens often causes the Chrome instances to crash.

Presumably, this is why Google introduced Headless Chrome. They call it “running Chrome without Chrome”, meaning that you can load and interact with web pages without having a visible UI shell or a physical screen display.

Headless mode has been available on Mac and Linux since Chrome 59 and on Windows since Chrome 60. The ability to use the Headless mode when running tests has since been integrated into various testing API libraries including Chrome CLI, Node.js libraries like Karma, Google’s own Puppeteer, Nightmare, and Selenium ChromeDriver.

The following coding examples were tested using Chrome 68, Chromedriver 2.40 and Watir 6.11.0.

Headless Chrome initialization

The following example shows a simple Chrome browser initialization followed by browsing to the MyHeritage home page, using Watir.

require 'watir'

browser = Watir::Browser.new(:chrome)
browser.goto('www.myheritage.com')

Running this sample results in the launching of a new Chrome browser instance and automatically browsing to myheritage.com.

Now, let’s alter this code so it does that in Headless mode.

require 'watir'

browser = Watir::Browser.new(:chrome, {:chromeOptions => {:args => ['--headless', '--window-size=1200x600']}})

browser.goto('www.myheritage.com')

As you can see, we’ve applied changes to the browser initialization call, including specifying the :chromeOptions argument for Headless mode. We also added the — window-size argument.

Why would a browser with no visual shell need a window size definition?

Here is a key concept for Headless mode: The fact that you don’t see a visual representation of the browser doesn’t mean the pages being viewed are not being built. In fact, the dimensions defined by the browser determine the pages layout and functionality, and in turn, the success or failure of the tests. If we want to test the functionality of a page in desktop layout, we need to make sure it is rendered in desktop screen dimensions.

This is actually useful in cases where you want to take screenshots. Yes, you can take screenshots in Headless mode! Taking screenshots is a common approach in web testing, even when the browser is visible and is arguably even more important when it isn’t.

Let’s add a line of code to take a screenshot right after we browse to the MyHeritage home page:

browser.driver.save_screenshot('screenshot.png')

Now let’s run the sample.

First of all, you’ll notice the absence of a browser. Although an instance of Chrome can be seen in your task manager, no actual browser is being opened while you wait. The execution is over when you get a “Process finished with exit code 0” message, at which time a screenshot.png file is created at the same location where your script is running.

When you open it, you’ll get a full picture of what happened while you were staring at a blank screen:

screenshot.png — This is not visually seen when the code is running

Headless Chrome initialization with mobile emulation

One of the most useful capabilities provided by the Chrome browser is the ability to emulate what a certain website looks like on mobile devices. This ability is not lost in Headless mode.
Let’s look at this code, running the same very simple test we did before, but this time, emulating the run as if on an iPhone 6 device:

require 'watir'

mobile_emulation = {:deviceMetrics => {'width' => 375, 'height' => 667, 'pixelRatio' => 2}, :userAgent => 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1'}

browser = Watir::Browser.new(:chrome, {:chromeOptions => {:mobileEmulation => mobile_emulation, :args => ['--headless']}})
browser.goto('www.myheritage.com')
browser.driver.save_screenshot('mobile_screenshot.png')

When comparing this version and the desktop version, you’ll see the :mobileEmulation addition to the Chrome options definition, set to accommodate the screen size and user agent for iPhone 6.

If we look at the resulting screenshot, we’ll see the MyHeritage home page as seen on an iPhone 6 device:

mobile_screenshot.png — Running headless with mobile emulation

For a full list of mobile screen size and user agent definitions, you can see DeviceDescriptors.js from Google’s Puppeteer on GitHub

Headless mode effect on run time and performance

If your testing environment is very limited in resources, you may be thrilled to save the CPU it takes to paint the actual browser screen. However, testing environments are rarely that poor in resources, and the CPU savings are rarely considered meaningful.

For us, the main goal when we introduced the Headless mode into our testing environment, was to cut down the time it takes automations to run. With our very large and continuously growing test suite, a reduction of 20% in run time would have been considered a huge success. In reality, the run time was only reduced by ~5%, making Headless Chrome a nice feature to have, but not a true game changer.

Furthermore, although Google claims that the Headless browser works just the same as the original, there is always the small disadvantage of running tests which do not accurately mimic a natural user environment.

All things considered, we decided to have our scheduled daily automation — which consists of the full tests suite — run in Headless mode (for the small, yet indisputable present run time improvement), while the manual on-demand runs, used throughout the day by developers and QA engineers, is set to run non-Headless by default, with on optional “Headless” setup.

For a more significant run time reduction we are currently researching other solutions, like Puppeteer — Google’s own node API — to control Headless Chrome or Chromium over the DevTools Protocol.

--

--