Skip navigation

Ever since I took it to heart to write unit tests for my code, I’ve wondered how to unit test frontend functionality. Some will say (and this I remember from my early readings on unit testing) that frontend is not meant to be unit tested but, instead, should be end-to-end tested but I respectfully disagree.

(I’d admit though, that I’ve never groked the difference between unit tests and end-to-end tests. They’re all tests to me in that they ensure my implementation is up to spec and that my changes did not break existing functionality. The last part has saved my ass so much for Chess Templar.)

I disagree because frontend code has features that lends itself well to being a unit-testable component. Among them:

  • Creating DOM objects is creating objects. You should be able to assert the properties of the created objects given parameters x and y.
  • Frontend validation. You should be able to assert how your validators behave. All the more true as your validation logic becomes more complicated.
  • It is not uncommon to have a util.js file. These are utilities, possibly used across multiple files and by multiple developers. It better be tested!

Disappointingly, Javascript has remained trapped inside browsers. Sure enough, Node and company are working to do justice on that but the fact remains that not everyone is using Node and company and so their Javascript remain trapped inside browsers.

And it seems that learning how to unit test in Javascript is largely dependent on how you use Javascript. Are you using plain old jQuery? AngularJS, maybe? For my intents and purposes, I’m on plain old jQuery.

It shouldn’t come as a surprise then that I ended up choosing QUnit as my unit testing library. The docs are easy-to-understand enough. With that, you’ll be testing your Javascript in no time!

But then comes the part of automation. The project I am using this for is, quite frankly, small in scope and it would not cost me much neurons to keep in mind to fire up the HTML page of a test-runner I got from reading QUnit’s docs every time I modify my scripts. But a good programmer is a lazy programmer. So I tried to automate that the tests run at Travis as well1.

The easiest guide I can find comes from this StackOverflow answer. The answer assumes an Ant project and the original question is even for Atlassian Bamboo. However, with a few tweaks, I got it to work with the free set-up the internets afford me.

First, you’d have to install xvfb in your environment. Xvfb is crucial for making your tests headless—I’ve actually first encountered it when trying to automate Selenium tests.

What’s fantastic with CIs (done properly) is that your code gets a fresh instance to run on everytime. Though that also means setting up (meticulously) everytime as well; this means ensuring that xvfb and other dependencies like Firefox or a MySQL instance is running before your tests are ran. To do that in Travis, I had the following in my .travis.yml:

language: python
python:
    - "2.7"
before_script: "pip install -r test-requirements.txt"
install: "pip install -r requirements.txt"
script: "nosetests --with-xcoverage && ./run_js_tests"
before_install:
    - sudo apt-get update -qq
    - sudo apt-get install -y xvfb
    - sudo apt-get install -y firefox
    - sudo apt-get install -y mysql-server
    - sudo /etc/init.d/mysql start
    - sudo apt-get install -y mysql-client
    - mysql -uroot -e "CREATE DATABASE alexandria_test;"

Notes:

  • I had to install with the the -y flag. This automatically answers “yes” to all installation-related queries.
  • I had to start mysql manually.
  • I also need to create the test database manually. A few years ago I would be very uncomfortable with the hard-coded test DB name; I would want it to get the name from my config.

Next, we’re using JS Test Driver. There are a couple of Javascript libraries JAR file in the Google Code repo I linked that you need. If you are using git (as I am) and would prefer to submodule, I had the repo ported to git here. I have not yet found the time to submodule since my goal was just to get the set-up working properly.

Having all the required libraries, all the rest are just some configuration. The main call would then be,

java -jar ../lib/JsTestDriver.jar --config jsTestDriver.conf --port 4224 --browser $FIREFOX --tests all --testOutput $OUTPUT_DIR

Where --port is as configured in jsTestDriver.conf and $FIREFOX is the path to the local Firefox installation.

Next come the actual unit tests. If you have followed thus far, you should be able to see some logs related to Javascript tests in your CI server. However, if you followed QUnit’s documentation as it stands (and be able to run your tests from an HTML page test runner), you might notice that your CI reports of 0 tests found and 0 tests ran.

This is because the QUnitAdapter library in JS Test Driver features a different organization than the actual QUnit library. I had to modify my unit tests as in this commit. But in summary:

  • Don’t refer from QUnit. QUnitAdapter provides window-level (read: global) functions that are analogous to QUnit’s. So QUnit.test becomes test and QUnit.module just becomes module.
  • Similarly, forego the asserts. A notable exception is the equality assertion. QUnit’s assert has it as equal while QUnitAdapter has it as equals.
  • beforeEach becomes setup. Self-explanatory.

I figured this out because I read the source code of QUnitTestAdapter. There might be more quirks I haven’t found out yet but these should be enough to get you to testing functionality; the unit test code might not be as idiomatic as desired but hey, it works.

I’m still far from my ideal unit testing set-up for Javascript. All I’ve managed to achieve in this post is to get the tests to run in a CI environment. Notably, test failures for Javascript do not affect the result of the build. I’d also want to get the Javascript included in the coverage reports. And, maybe, reconcile QUnitTestAdapter with the actual constructs of plain QUnit.

  1. Interesting to note that jQuery does not have CI badges on its repo and that they use Node to automate. []

Leave a Reply

Your email address will not be published. Required fields are marked *