Browserless AJAX Testing with Rhino and Envjs, Part 2.
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-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
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
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!
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.
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.