Friday, October 2, 2009

Testing Javascript UI

This has been something I've been thinking a long time about. The key about testing javascript, anything in general in fact, is designing your code so it's highly modular, so you can test small, single-purpose components by themselves, and then combine them to test system-wide functionality. Mocks are great in this respect - if your components need another object, mocks can provide that object with no implementation, rather, just give you the methods and properties you need to test the cases you want to test.

The wrinkle that comes in testing javascript versus "easy" test subjects like Java or other non-ui languages is the fact that Javascript's nature is so intertwined w/ UI. So, a lot of times what you're testing is not as much the javascript code, as the resultant DOM that the javascript code manipulates in the function that you call. This can become quite a hard thing to test - and you need to really ensure that your checking of the DOM is correct - which may require further testing.

I think the solution to above is prototypes - I have dealt with these before, as that's how at my last job we implemented most of the portlet and other advanced functionality in the product - through the use of blank, generic blocks of DOM that we clone, manipulate and inject into the page DOM. These prototypes are IDed - thus can only appear once on the page. The key is to get the right prototype, clone it, change it as we needed it, and inject it into the DOM - thereby "creating on the fly" a new and unique DOM element.

Adapting this technique to testing: create a number of raw prototypes - these should be the SAME prototypes you're currently using in your code. To ensure you're using the same prototypes, you might want to actually have a build-time step to join a single-set of prototypes to both the app pages as well as tests (which implies that tests should ALWAYS run during build).

So, using these prototypes, you would create a set of DOM elements, which will be the END RESULT, as you have designed your DOM to be, of what will happen once these prototypes go through the code and be injected into the page's DOM.

Coming back to the javascript portion of javascript testing, ideally, your methods are small and change only certain parts of the DOM. With the "resultant prototypes" produced from previous step, you can compare them using a simple recursive tree routine with those DOM elements produced by your javascript - simply go down attribs and elements, to see that they match. If these match, you're all set, if not, well, you've got work to do :)

This has a several positive effects - first, your code will have unit tests - something that most javascript doesn't - which is unacceptable. Secondly, your code will be that much more regression proof - especially as libraries get re-used (which they will more, if they are more granular). Third, and perhaps more important, you will do your design upfront, and have a great idea about not only what your code will look like, but will also have an opportunity to optimize your DOM before writing any code - thereby making your code more compact and efficient.

Check out the following links:
- http://www.infoq.com/articles/javascript-tdd
- http://www.jsunit.net/
- http://jsmock.sourceforge.net/