Building complex programs is just a matter of breaking it down into smaller units, and then putting them together. Unit testing is the testing of those smaller units. If you haven’t written unit tests for your code yet, you should. It’s worth the effort. It helps you think through the expectations of your code in an organized way, minimizes risk and effort when changing that code, and encourages modular design — which has its own benefits.
There are enough good unit testing frameworks out there that you don’t have to roll your own. If you want a recommendation, try QUnit. It’s used by the jQuery suite of products, it’s mature, it’s easy-to-use, and they have an excellent tutorial.
If you’re the type who likes choices, here are some alternatives:
The most important thing is that you pick one, learn how to use it, and then start. Once you’ve done that, you’ve already realized 98% of the value. If you need that extra two percent, then go ahead and spend time figuring out which test framework suits you best—although it’s not necessary.
The idea of unit testing is to test a small standalone unit of code before integrating it into the bigger system. That means you have to have standalone units to test — code with few dependencies outside of itself, if any. If you’re writing the code from scratch, that means writing it in a modularized, loosely-coupled way. If the code already exists, then you may need to do some amount of refactoring to make modular and loosely-coupled enough to add unit tests.
One of my goals when writing unit tests for the browser is that it’s testable without starting my app server. I want to be able to unit test with, at most, a static web server. I think that’s a good goal to have.
The test harness for your browser unit tests is a separate web page which includes a JS file containing your test cases. Although there’s no hard rule for how to organize them, I recommend testing each JS file separately. That means matching each of your JS files to another containing its test cases plus an HTML page to harness the test cases. I also like to have one master HTML page to include all of the tests. That way, you can run all tests before each build/release, but limit it to just the tests for a specific module while you’re actively making changes.
Your directory structure may look something like this:
webapp/ |- css/ |- img/ |- js/ | |- menu.js | `- calendar.js |- test/ | |- allTests.html /* includes all your test cases */ | |- menuTest.html /* includes menuTest.js test cases */ | |- menuTest.js | |- calendarTest.html /* includes calendarTest.js test cases */ | `- calendarTest.js `- index.html
Most unit test frameworks have some feature to do some setup work before and after your test suite is run, or before and after each individual test case is run. This is commonly referred to as “setup” and “teardown”. This is especially useful to test actions that required a specific DOM structure, allowing you reset the DOM before each test.
QUnit even has a feature where you can stick the required DOM elements into a DIV with id=qunit-fixture, which get automatically reset before each test. It’s described in their cookbook in the recipe for Keeping Tests Atomic.
AJAX requests and other synchronous requests need special handling. You have to indicate to the test framework that you will be running an asynchronous test and then signal to it when the test is complete. Otherwise, the test framework would jump onto the next test, and possibly run any setup and teardown activities prematurely.
In QUnit asynchronous tests look like this:
I mentioned earlier that one of my goals when writing unit tests for the browser is that it’s testable without starting my app server. I want to be able to use a simple static web server to do my testing. It makes for quicker development. An area that needs special treatment in this regard is that of HTTP requests.
Without the dynamic responses from an app server, I stub the responses by doing two things:
- Mocking all the static responses I need for my test cases, and
- Making the callback URL for your components configurable real-time so they can be pointed to the stubbed responses in the middle of the tests.
That’s all I have to say about unit testing in the browser for now. So what are you waiting for? Go pick up a test framework and get to work!