I must admit, the first time I heard about headless browser testing, I had zero knowledge of the technology. As I started to learn more about headless browser testing and compared it with Selenium, it quickly came to my attention that both are different, and both have different objectives. There is no rivalry or battle; both testing frameworks serve a purpose in your delivery chain.
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.1)“Headless browser – Wikipedia, the free encyclopedia.” 2015. 1 Jun. 2016
Selenium is a portable software testing framework for web applications. Selenium also provides a record/playback tool for authoring tests without learning a test scripting language.2)“Selenium (software) – Wikipedia, the free encyclopedia.” 2011. 1 Jun. 2016
Let’s take a closer look at CasperJS (headless browser testing framework) and how we can add another testing layer to our delivery chain.
CasperJS is a JavaScript scripting and testing framework built on top of PhantomJS (WebKit) and SlimerJS (Gecko). What is a WebKit, and what is Gecko? WebKit is an open source browser engine for rendering web pages in Safari, Chrome, and Opera. Gecko is the Firefox browser engine. The web browser engine renders content such as HTML, XML, images, and formats information (CSS). CasperJS is a testing utility for functional navigation, page status, network monitoring, screen capture, and scraping data off the web page. CasperJS allows assertions, which are an easier way to track failed tests.
The CasperJS API has an extensive collection of features available. The most important of the features is the tester module. For one of my recent projects, I wanted a lightweight pre-commit test to check the page HTTP status code after deployment. Calling the assertHttpStatus() function was a perfect testing solution for me. Here is a list of all the CasperJS modules:
Module
Description
Easy way to create Casper instance and pass Casper options
Client-side utilities are injected in the remote DOM environment
Generates ANSI color output on the console screen
Mouse operations like moving, clicking, double-clicking, rollover, etc.
Unit and functional testing assertions
Simple helper functions
I highly recommend you check out the online API documentation when evaluating a headless browser testing framework before making your final decision. The CasperJS online documentation is hands-down one of the best-documented testing frameworks out there. Each function has an excellent description with an example.
To get started, you need to properly install Casper, and then we can write our first page http status code test.
Let’s dive right into it. For the sake of this article, I have initialized the base URL variable and a few arrays for links, page titles, and page by name inside the test file.
[code language=”js”]var baseUrl = ‘http://www.saucelabs.com’;
var links = ["/resources","/features","/our-values","/enterprise",];
var saucePageTitles = ["Sauce Labs: Resources","Sauce Labs: Features","Sauce Labs: Values","Sauce Labs: Enterprise-grade testing on Sauce",];
var saucePageByName = ["Resources", "Features", "Company", "Enterprise"];[/code]
To keep it simple, the test will execute each element of the array. The function ‘casper.start().repeat(nTimes, function()’ begins to run the test and will repeat (loop) through the test a given number of times. The variable nTimes equals the number of elements in the ‘links’ array (which is 4). Then function ‘casper.thenOpen(baseUrl + links[i], function()’ will open a new location, which in our case is the baseUrl + links[0] (http://saucelabs.com/resources) and execute assertions and screen capture lines of code within the function. Lastly, the most important function ‘casper.run(function()’ runs the whole suite of steps and executes a callback when they’ve all been done. FYI – Casper suite will not run without a ‘run()’ function.
[code language=”js”]casper.test.begin(‘Sauce Labs Pages HTTP Status Code’, function suite(test) {
var i = 0;var nTimes = links.length;
casper.start().repeat(nTimes, function() {
casper.thenOpen(baseUrl + links[i], function() {var status = this.currentHTTPStatus;var title = this.getTitle();
// Assertionstest.assertTitle(saucePageTitles[i], saucePageByName[i] + ‘ has the correct title’);test.assertHttpStatus(200,’HTTP Status: ‘ + status + ‘ – ‘ + baseUrl + links[i]);
// Capture the current screenvar currentPageName = saucePageByName[i].toLowerCase();this.capture(‘./screenshots/mlb-‘ + currentPageName + ‘-pg-httpstatus.png’);console.log(”);i++;});});
casper.run(function() {test.done();});});[/code]
Now that we have created an HTTP status test, let’s run it.
Run CasperJS Test from Command Line
[code language=”js”]casperjs test tests/httpstatus.js[/code]
Example: Console for SUCCESSFUL Tests
[code language=”js”]Test file: tests/httpstatus.js# Sauce Labs Pages HTTP Status CodePASS Sauce Labs Pages HTTP Status Code (NaN test)PASS Resources has the correct titlePASS HTTP Status: 200 – http://www.saucelabs.com/resources
PASS Features has the correct titlePASS HTTP Status: 200 – http://www.saucelabs.com/features
PASS Company has the correct titlePASS HTTP Status: 200 – http://www.saucelabs.com/our-values
PASS Enterprise has the correct titlePASS HTTP Status: 200 – http://www.saucelabs.com/enterprise
PASS 8 tests executed in 20.644s, 8 passed, 0 failed, 0 dubious, 0 skipped.[/code]
Example: Console for FAILED Test
[code language=”js”]FAIL Enterprise has the correct title# type: assertTitle# file: tests/sauce/httpstatus.js:28# code: test.assertTitle(saucePageTitles[i], saucePageByName[i] + ‘ has the correct title’);# subject: "Sauce Labs: Enterprise-grade testing on Sauce"# expected: "Sauce Labs: Enterprise"FAIL 7 tests executed in 19.263s, 6 passed, 1 failed, 0 dubious, 0 skipped.[/code]
It is easy to see that the console outputs for successful and failed tests are informational. I’ll focus mainly on the failure exception stack trace, which provides enough information on why the CasperJS assertion failed. It clearly identifies that the page title response doesn’t match the expected page title defined inside the test.
The main inspiration behind this article was to help everyone identify that both headless and real browser testing are different. I also wanted to show how simple it is to write an http status test, and show that headless browser testing serves a purpose of everyone in the continuous integration pipeline. It takes a minimal amount of effort to design an acceptance test suite through CasperJS.
Greg Sypolt (@gregsypolt) is a Senior Engineer at Gannett – USA Today Network and co-founder of Quality Element. He is a passionate automation engineer seeking to optimize software development quality, while coaching team members on how to write great automation scripts and helping the testing community become better testers. Greg has spent most of his career working on software quality—concentrating on web browsers, APIs, and mobile. For the past five years, he has focused on the creation and deployment of automated test strategies, frameworks, tools and platforms.