Browserless AJAX Testing with Rhino and Envjs, Part 2.
Introduction
This is the second of two posts looking at browserless AJAX / JavaScript testing with Rhino and Envjs. Last time, we set up Rhino and Envjs to run a basic set of QUnit tests from the command line, exercising pure JavaScript code with no AJAX or DOM manipulation. Today, we will add tests for code that hacks on the DOM and makes AJAX calls.
Let’s start with the files we had last time (with links to previous blog post):
env.rhino.1.2.js
: Envjs for Rhino.qunit.css
: QUnit styles. (Link to known working version).qunit.js
: QUnit code. (Link to known working version).setup.js
: Our custom hook Envjs to QUnit.run-tests.js
: Include our setup script and actually invoke the tests at on our test HTML page.my-lib.js
: An example JavaScript library (that we want to test).my-tests.js
: An example custom QUnit tests for my-lib.js.test.html
: Basic QUnit HTML page (see QUnit documentation) with script links to “my-lib.js” and “my-tests.js”.
New Files and Tests
Let’s add some new libraries and tests to our working set, to include DOM actions and AJAX:
jquery.js
: The venerable jQuery library, which we’ll use for DOM and AJAX calls.my-tests2.js
: Our new tests, described in full detail below.- my-lib2.js: We’ll add two functions – one which does simply DOM manipulation with events, the other which does an AJAX call and inserts the text from another URL into a div element.
Interestingly, I originally had the dumpText()
function create a new
“<pre/>” tag, but something between Rhino and Envjs did not like it, and
Rhino would error out on running this code. So, I switched to “<code/>”
and everything worked fine. I guess the point is that nothing absolutely
substitutes for a real browser, but Envjs does a really great job
notwithstanding!
Serving Our Files for AJAX
One important thing to note is that because we are doing AJAX calls now, we quickly will run into the same origin policy. To easily get around this for purposes of this demonstration, open a new terminal window, point it to the test directory we’re working in and serve all files in the directory directly from localhost with the python built-in web server (here I chose port 8001):
Check things out by navigating a real browser to: http://127.0.0.1:8001/test.html. Our QUnit tests from last time should run in the window.
Testing our New Code
Now that we have created our new library and are running a slim web server (so we can actually make AJAX requests), let’s write some test code in “my- tests2.js”. At the module level, we make use of the QUnit setup and teardown options to attach test div elements to the QUnit fixture element, so we can actually do some (temporary) DOM manipulation.
We use an asynchronous test pattern for the dumpText
AJAX tests to handle
latency between the call and response. The call to stop()
stops QUnit
execution to allow the code and tests to run. If the asynchronous callback
doesn’t finish (and invoke start()
) within the designated time period
(here 1 second), then QUnit will raise an error. Assuming we have given
ourselves enough time, once start()
is invoked, then QUnit starts up again
for the remaining tests and a final summary. For a good overview of QUnit
testing with asynchronous examples, see
“How to Test your JavaScript Code with QUnit”.
Here’s everything together for “my-tests2.js”:
Now, lets update our “test.html” file to run both our previous and new unit tests (and add the jQuery dependency):
Finally, we need to tweak “run-tests.js” to point to the locally served test page instead of simply the local filesystem.
And, let’s run the tests!
From the results, we can see that we now have all of our unit tests running from pure JavaScript code to our new DOM manipulation and AJAX calls.
Other Enhancements
Beyond the setup we discussed today, there are several really cool additions and improvements you can add to your browserless testing solution.
Use Mocks for AJAX Calls
Although running a simple python web server gets us through this demonstration pretty easily for our AJAX calls, in practice AJAX calls might rely on more than a simple static server can provide. At that point, you either have to deal with running your development web application server at the same time, or switch to mocking out the AJAX calls. A great solution is to use fantastic MockJax jQuery plugin, which overrides $.ajax() calls to substitute with a configurable mock response.
Add Code Coverage Reports
Amazingly, the JSCoverage code coverage library does work with this Rhino and Envjs setup. I hooked things up by running jscoverage-server in a similar fashion to how we did the command line python server above (which completely replaces the need for it), and then added the following to “setup.js” in the QUnit.done option:
It is important that the coverage report come at the very end of the QUnit.done handler to ensure that all of our tests truly are finished running. See the JSCoverage manual for more details about running the coverage server and writing reports.
Conclusion
Revisiting the original motivation for this post series, the original pain point was that frontend tests are complicated to write and a hassle to run. With the fully browserless testing environment we have explored here, it is possible to create a system out of Rhino, Envjs and QUnit that runs all tests with a single command line argument, making the former hassle into something so easy you could add it to your pre-/post- commit hooks in your version control software. And, by being able to write our tests in mostly straight QUnit (with a little extra setup help), test writing for developers remains relatively straightforward, even dealing with AJAX, etc.