Headless
I have noticed a lot of confusion about headless testing, but there is really nothing special there. The confusion may be caused by the term headless being a bit vague. In the context of driving a browser, headless means you can drive a real browser, but without seeing anything on the machine. It can be useful for running tests on a headless machine, or on a desktop machine. For example, if you want to run tests on a headless machine as part of continuous integration, or if you want to run tests on your desktop machine without browsers opening and closing on the screen all the time, while you are doing something else.
There are two ways to run browser in a headless mode that I am aware of. One is to use a headless browser like PhantomJS. Another option is using Xvfb (X virtual framebuffer) and Headless gem. Please notice that Xvfb works only on Linux.
The advantage of running tests in PhantomJS is that it is supported on all major operating systems. The browser is pretty good, it has the same Selenium API as all other browsers, so the vast majority of the tests developed using another browser will just work. It also has a pretty good JavaScript engine, so even JavaScript heavy pages should work fine. The browser is also faster than other browsers. More speed is always good, but speed improvement depends on a lot of things, so sometimes you will see a lot of improvement, and sometimes just a few percent.
The disadvantage is that the users of your web application are not using PhantomJS to access it, they are using one of the major browsers. Sometimes you will have to tweak the tests or the application to get all tests to run fine. It is also harder to debug failures, since the browser is headless. Fortunately, you can take screen shots and HTML of the page (the entire page or just the relevant part).
The advantage of using Xvfb is that it works with any browser that Selenium can drive. You can delevop tests using your favorite browser and then run them in headless mode with no modifications. The disadvantage is that is it somewhat slower than PhantomJS (but not a lot), and it works only on Linux. Let me repeat that, Xvfb does not work on Windows or Mac OS.
PhantomJS
|
You will need internet access if you want to follow examples in this chapter. All examples in this chapter are tried on Mac OS X 10.8.5, PhantomJS 1.9.7, Ruby 2.1.1p76 and selenium-webdriver 2.40.0 but everything should work on all supported platforms. |
If you do not have Ruby, Selenium and PhantomJS already installed, please see Installation chapter. If you are not familiar with Selenium API, please see Quick Start, Driver and Element chapters.
All you have to do to drive PhantomJS is to let Selenium know that you want to drive it:
1 $ irb
2
3 > require "selenium-webdriver"
4 => true
5
6 > browser = Selenium::WebDriver.for :phantomjs
7 => #<Selenium::WebDriver::Driver:...
8 browser=:phantomjs>
The rest of the API is the same as for any other browser. For example, go to a page:
1 > browser.get "http://google.com"
2 => {}
Get it’s URL and title:
1 > browser.current_url
2 => "http://www.google.hr/"
3
4 > browser.title
5 => "Google"
Enter text into the text field and then clear the text field:
1 > browser.find_element(name: "q").send_keys "watir"
2 => nil
3
4 > browser.find_element(name: "q").clear
5 => nil
Play with element attributes:
1 > browser.find_element(name: "q").attribute(:name)
2 => "q"
3
4 > browser.find_element(name: "q").attribute(:class)
5 => "lst tiah"
6
7 > browser.find_element(name: "q").attribute(:type)
8 => "text"
9
10 > browser.find_element(name: "q").
11 attribute(:autocomplete)
12 => "off"
When driving Firefox or any of the usual browsers, you are able to see how to page looks like. With PhantomJS you do not see anything, so it is really important to know how to debug problems. Two features are really useful, taking screenshots and getting page HTML.
To take a screenshot, use save_screenshot method:
1 > browser.save_screenshot "phantomjs.png"
2 => #<File:phantomjs.png (closed)>
To get page HTML, use
1 > browser.page_source
2 => "<!DOCTYPE html><html itemscope=\"\"
3 itemtype=\"http://schema.org/WebPage\"><head><meta
4 itemprop=\"image\"
5 content=\"/images/google_favicon_128.png\">
6 <title>Google</title><script>
7 ...
8 </script></body></html>"
To get HTML of just a part of the page, ask for outerHTML attribute:
1 > browser.find_element(name: "q").
2 attribute(:outerHTML)
3 => "<input autocomplete=\"off\" class=\"lst tiah\"
4 ...
At the end, close the browser:
1 > browser.quit
2 => nil
Create a Ruby file from the above IRB session and save it as headless_phantomjs.rb. Of course, add a p in front of a few commands, so the script outputs something.
1 require "selenium-webdriver"
2 browser = Selenium::WebDriver.for :phantomjs
3 browser.get "http://google.com"
4 p browser.current_url
5 p browser.title
6 browser.find_element(name: "q").send_keys "watir"
7 browser.find_element(name: "q").clear
8 p browser.find_element(name: "q").attribute(:name)
9 p browser.find_element(name: "q").attribute(:class)
10 p browser.find_element(name: "q").attribute(:type)
11 p browser.find_element(name: "q").
12 attribute(:autocomplete)
13 browser.save_screenshot "phantomjs.png"
14 p browser.page_source
15 p browser.find_element(name: "q").
16 attribute(:outerHTML)
17 browser.quit
Run the file:
1 $ ruby headless_phantomjs.rb
2 "http://www.google.hr/"
3 "Google"
4 "q"
5 "lst tiah"
6 "text"
7 "off"
8 "<!DOCTYPE html><html itemscope=\"\" itemtype=...
9 "<input autocomplete=\"off\" class=\"lst tiah\"...
Xvfb
|
You will need internet access if you want to follow examples in this chapter. All examples in this chapter are tried on Ubuntu Linux 13.10, Firefox 27.0.1, Ruby 2.1.1p76 and selenium-webdriver 2.40.0 but everything should work on all supported platforms. |
|
Xvfb works only on Linux. It does not work on Windows or Mac OS. |
If you do not have Firefox, Ruby or Selenium installed, see Installation chapter. If you are not familiar with Selenium API, please see Quick Start, Driver and Element chapters.
Without getting into a lot of technical detail, Xvfb (X virtual framebuffer) is a piece of software that makes is possible to run browsers (and other applications) in a headless mode.
Install Xvfb via apt-get:
1 $ sudo apt-get install xvfb
2 ...
Then install headless Ruby gem:
1 $ gem install headless --no-ri --no-rdoc
2 ...
|
“To sudo or not to sudo, that is the question…”
|
There are two modes of using headless gem, block and object. Block mode will automatically start and destroy headless session. In object mode, you have to explicitly start and destroy the session.
This is an example of block mode (using IRB):
1 $ irb
2
3 > require "headless"
4 => true
5
6 > require "selenium-webdriver"
7 => true
8
9 > Headless.ly do
10 > browser = Selenium::WebDriver.for :firefox
11 > browser.get "http://google.com"
12 > browser.title
13 > end
14 => "Google"
This is an example of object mode (using IRB):
1 $ irb
2
3 > require "headless"
4 => true
5
6 > require "selenium-webdriver"
7 => true
8
9 > headless = Headless.new
10 => #<Headless:0x9e957d8 @display=99,
11 @autopick_display=true, @reuse_display=true,
12 @dimensions="1280x1024x24",
13 @video_capture_options={}, @destroy_at_exit=true>
14
15 > headless.start
16 => #<Proc:...>
17
18 > browser = Selenium::WebDriver.for :firefox
19 => #<Selenium::WebDriver::Driver:...
20 browser=:firefox>
21
22 > browser.get "http://google.com"
23 => ""
24
25 > browser.title
26 => "Google"
27
28 > headless.destroy
29 => ["/tmp/.X99-lock"]
Of course, block and object mode can be used in Ruby files, not just in IRB. Save the following text as headless_block_mode.rb file.
1 require "headless"
2 require "selenium-webdriver"
3
4 Headless.ly do
5 browser = Selenium::WebDriver.for :firefox
6 browser.get "http://google.com"
7 p browser.title
8 end
Run the file:
1 $ ruby headless_block_mode.rb
2 "Google"
Save the following text as headless_object_mode.rb file.
1 require "headless"
2 require "selenium-webdriver"
3
4 headless = Headless.new
5 headless.start
6
7 browser = Selenium::WebDriver.for :firefox
8 browser.get "http://google.com"
9 p browser.title
10
11 headless.destroy
Run the file:
1 $ ruby headless_object_mode.rb
2 "Google"
As usual, you can take screenshots using Selenium API while running tests, even in headless mode. If you do not know how to do that, see Driver chapter.
Xvfb has it’s own screenshots and video recording API, but it is beyond the scope of this book to cover it. For more information see documentation for headless gem.