(For more resources related to this topic, see here.)
When developing with JavaScript, we often need to chain two pieces of code (for instance, first we load some JSON data, and then we update the page content using that data).
But each step is generally non-blocking (meaning the rest of the code will continue to execute even if the step is not complete), and there is no way to predict when the first step will be complete.
The most solid and most common approach to solve this problem is the callback mechanism. We put the second piece of code in a function, and pass it as a parameter to the first one, so that it can call this function when it finishes.
As a result, there is no linear and predictably-ordered execution of the code, which makes testing a little bit tricky.
The following is an example.
<html> <head> <script type='text/javascript' src = 'http://code.jquery.com
/jquery-1.9.1.js'></script> <style> .searching { color: grey;} .success { color: green;} .noresults {color: red;} </style> </head> <body> <script> function geonamesSearch() { $('#results').html("Searching..."); $('#results').attr('class', 'searching'); var url = "http://ws.geonames.org/searchJSON"; var query = $('#searchedlocation').val(); $.getJSON(url + "?q="+ query +"&maxRows=25&featureClass=P", null, function(data) { var data = data.geonames; var names = []; if(data.length > 0) { $.each(data, function(i, val){ names.push(val.name +" ("+val.adminName1+")"); }); $('#results').html(names.join("<br/>")); $('#results').attr('class', 'success'); } else { $('#results').html("No matching place."); $('#results').attr('class', 'noresults'); } } ); } </script> <input type="text" id="searchedlocation" /> <button id="search" >
This page contains a text input field, a button, and an empty div whose ID is ‘results’. When we click on the button, the JavaScript function geonamesSearch does the following:
We can try it with our web browser, and see it works nicely.
Now let’s test this page with the following CasperJS script which enters the value ‘barcelona’ and asserts we do get Barcelona (Catalonia) in the results:
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X)'); casper.test.begin('Search a city by name', 1, function(test) { casper.start('http://localhost:8000/example3.html', function() { this.sendKeys("input#searchedlocation", "barcelona"); this.click("button#search"); }); casper.then(function() { test.assertTextExists('Barcelona (Catalonia)',
'Barcelona (Catalonia) has been found.'); }) casper.run(function() { test.done(); }); });
Note that we need to set up a regular user agent to make sure geonames.org will accept to process our request.
If we run it, we get a failure:
Why is that? It is because our this.click() triggers the geonamesSearch function and immediately after we try to assert the result content, but the Geonames web service had no enough time to respond, the content is not yet the one expected at the time the assertion is performed.
The CasperJS then() blocks allow to make sure we will execute our test steps sequence in the right order, but they cannot guarantee that an asynchronous call performed in one of the block will be complete before we move to the next block.
To manage this kind of cases, CasperJS offers the ability to wait before executing the rest of our test sequence.
Here is a working test script:
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X)'); casper.test.begin('Search a city by name', 1, function(test) { casper.start('http://localhost:8000/example3.html', function() { this.sendKeys("input#searchedlocation", "barcelona"); this.click("button#search"); }); casper.waitForSelector('div.success', function() { test.assertTextExists('Barcelona (Catalonia)',
'Barcelona (Catalonia) has been found.'); }) casper.run(function() { test.done(); }); });
We can see the tests pass successfully now:
With waitForSelector, we make sure the assertion will be performed only when the results div will have the ‘success’ class, and it will only happen once our JSON loading callback function has been called.
In this article, we learned how to test our use case with CasperJS and how timing is everything.
Further resources on this subject:
I remember deciding to pursue my first IT certification, the CompTIA A+. I had signed…
Key takeaways The transformer architecture has proved to be revolutionary in outperforming the classical RNN…
Once we learn how to deploy an Ubuntu server, how to manage users, and how…
Key-takeaways: Clean code isn’t just a nice thing to have or a luxury in software projects; it's a necessity. If we…
While developing a web application, or setting dynamic pages and meta tags we need to deal with…
Software architecture is one of the most discussed topics in the software industry today, and…