In the ten years I’ve been building websites professionally, I can’t count the number of times I have crafted something with all of the care and attention it deserved, manually testing and double-checking in each browser before and after deployment, only to wait a few weeks before hearing that it’s broken along with the latest round of features that was released. How can we stop this heartbreak from happening?
A tool that would check our work as we develop would be great. It should check on other parts of the site as well, so that we don’t break existing features when we add new ones. And if it were possible to write the tests in a language we’re familiar with, that decreases the time spent setting the tests up.
Enter CasperJS, a functional testing tool for the frontend. It is built upon JavaScript, the language we’re already using to power the web. It’s easy to learn and just as versatile as the browser itself.
Using CasperJS
If you’re already familiar with jQuery, Casper won’t be hard for you to pick up. The syntax isn’t too different, and Casper performs concrete actions in a very similar way to jQuery. However, instead of manipulating DOM elements to create an interface, we’re going to test that interface and verify that the website behaves exactly as we expect it to.
There are installation instructions and basic examples on the CasperJS website, so once you’re familiar with those, come back to jump right into the Test module of CasperJS. All of our examples in this series will be using the Casper 1.1.0 test syntax.
Basic components of a Casper test
These are the basic components of a CasperJS test file:
- test suite declaration: includes a test name, number of tests, and the
suite()
itself. - functional steps: all of the code inside the
suite()
function. Includes steps to perform actions on the website, and test assertions to verify if conditions match expectations. - run tests: the command which runs all the tests and generates the final report.
We can use this basic patte\r\n \to manipulate and test any interface, whether it’s the UI of a website or the API of an integrated service.
Testing responsive images with CasperJS
Responsive images are a normal part of websites nowadays because they allow lesser devices to save bandwidth and energy by downloading smaller media, while greater devices can opt for higher-resolution assets that match their rich displays. Therefore it’s important to know if your solution is working as intended, and also to have a warning system if another feature’s code begins to interfere with the responsive images code, so you can prevent errors from making it to your live site.
In order to demonstrate what we’re trying to achieve, perhaps it’s best to look at the results first. When all of the tests are run successfully, this Casper test produces the following output:
Walking through the test
There are five tests which must all pass for that final green line to show up with your triumphant PASS. Here are the five tests written out in plain english:
- Is there a
<picture>
element anywhere on the page? - Are there two
<source>
elements inside a<picture>
element? - At a viewport size of 320×480, is the medium.jpg displayed?
- At a viewport size of 960×640, is the large.jpg displayed?
- At a viewport size of 1280×1024, is the extralarge.jpg displayed?
Unless all five individual tests pass, the overall Testing Picturefill test will not report a pass. So, once again in plain english, here is the series of steps that Casper will complete in order to test Picturefill:
- Open the demo URL
- Look for a
<picture>
element - Look for two
<source>
elements inside a<picture>
element - Resize the browser viewport to 320×480
- Read the
src
attribute on the<img>
inside<picture>
to see if it contains ‘medium.jpg’ - Resize the browser viewport to 960×640
- Read the
src
attribute on the<img>
inside<picture>
to see if it contains ‘large.jpg’ - Resize the browser viewport to 1280×1024
- Read the
src
attribute on the<img>
inside<picture>
to see if it contains ‘extralarge.jpg’ - Finish test
Example script
The sample Picturefill test has inline comments for every action that is taken, so you can understand why each line is necessary. There are also links to the API documentation, so you can find more information about the commands used.
/** * @file * Testing to see if Picturefill selects the right source at multiple * viewport sizes. */ // Define the suite of tests and give it the following properties: // - Title, which shows up before any of the pass/fails. // - Number of tests, must be changed as you add tests. // - suite(), which contains all of your tests. // // @see http://casperjs.readthedocs.org/en/latest/modules/tester.html#begin casper.test.begin('Testing Picturefill', 5, function suite(test) { // casper.start() always wraps your first action. The first argument should // be the URL of the page you want to test. // // @see http://casperjs.readthedocs.org/en/latest/modules/casper.html#start casper.start('http://scottjehl.github.io/picturefill/examples/demo-02.html', function () { // First, we look for a <picture> element. The first argument is a query // selector, like jQuery or document.querySelectorAll(). Any <picture> tags // on the page are found by using the 'picture' selector. // // @see http://casperjs.readthedocs.org/en/latest/modules/tester.html#assertexists test.assertExists('picture', '<picture> element found.'); // Now verify that the <picture> tag has two <source> tags. This is // another query selector. In a more complex test you will need to write // a more specific selector. This will find all <source> tags within all // <picture> tags in your document. This demo only has one <picture> tag // so the simple selector works fine. // // @see http://casperjs.readthedocs.org/en/latest/modules/tester.html#assertelementcount test.assertElementCount('picture > source', 2); // Before running any viewport-specific tests, set the viewport to 320x480. // PhantomJS has a default viewport of 400x300 and CasperJS does not alter // this default, so it's always a good idea to explicitly set the viewport // to your desired dimensions. // // @see http://casperjs.readthedocs.org/en/latest/modules/casper.html#viewport this.viewport(320, 480); }); // casper.then() allows us to wait until previous tests and actions are // completed before moving on to the next steps. This is useful for many // situations and viewport resizing is one of them, since scripts like // Picturefill have to respond to the resize. // // @see http://casperjs.readthedocs.org/en/latest/modules/casper.html#then casper.then(function() { // With the viewport resized, look for the src of the <img> and make sure it // is set to the value we expect. In this example we are testing the smaller // viewport first, so medium.jpg should be found since the Picturefill markup // declares it as default. // // @see http://casperjs.readthedocs.org/en/latest/modules/tester.html#assertevalequals test.assertEvalEquals(function () { return document.querySelectorAll('picture img')[0].getAttribute('src').match('medium.jpg').toString(); }, 'medium.jpg', 'medium.jpg found using 320x480 viewport.'); }); // Resize the viewport again casper.then(function() { this.viewport(960, 640); }); // With the viewport resized to 960px, check and see if the large.jpg is // now contained within the <img> src, since the demo markup specifies an // 800px breakpoint for this image. casper.then(function() { test.assertEvalEquals(function () { return document.querySelectorAll('picture img')[0].getAttribute('src').match('large.jpg').toString(); }, 'large.jpg', 'large.jpg found using 960x640 viewport.'); }); // Resize the viewport again casper.then(function() { this.viewport(1280, 1024); }); // Finally, with the 1280x1024 viewport, check to see if the <img> is now // using the extralarge.jpg source which was specified for viewports larger // than 1000px wide. casper.then(function() { test.assertEvalEquals(function () { return document.querySelectorAll('picture img')[0].getAttribute('src').match('extralarge.jpg').toString(); }, 'extralarge.jpg', 'extralarge.jpg found using 1280x1024 viewport.'); }); // This code runs all the tests that we defined above. // // @see http://casperjs.readthedocs.org/en/latest/modules/tester.html#done casper.run(function () { test.done(); }); });
If you want to fork the code, here’s a gist.
Further reading
If you enjoyed this article, move on to Part 2 of our CasperJS series: Testing custom JavaScript code. You can also follow me on twitter to receive new articles as I publish them. Thanks for stopping by!
Part 2: Testing JS code with CasperJS
Making the web a better place to teach, learn, and advocate starts here...
When you subscribe to our newsletter!