This is the first post in a new blog post series I'm calling "Get started in 5 minutes". In this post, I'm going to describe how you can get started using jsUnit to unit test your client-side Javascript code in 5 minutes. jsUnit is a port of jUnit (a Java unit testing framework) and there is a lot more to jsUnit that I'm not going to get into (you can even test server-side code), but I think this will be enough for many of you to unit test your client-side code. In doing so, you can pracice Test Driven Development without wincing when your Javascript grows in complexity.
"Either Karate: yes, or Karate: no. No Karate: maybe so." -Karate Kid
Here are the steps to get started...
1. Download jsUnit (currently jsunit2.2alpha11.zip)
2. Extract the "jsunit" directory in the ZIP file into your web app folder.
3. Create a Javascript library containing the code to be tested. In this case, call it MyLibrary.js, put it in your web app folder and paste in the following code:
function setName(name)
{
document.all['name'].value = name;
}
4. Create a test page (similar to a test fixture in NUnit, etc.). In this case, call it TestMyLibrary, put it in your web app folder and paste in the following code:
<html>
<head>
<title>jsUnit test</title>
<script language="JavaScript" src="jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="MyLibrary.js"></script>
<script language="JavaScript">
function testSetName()
{
setName('Pat Gannon');
assert('name field set incorrectly', document.all['name'].value == 'Pat Gannon');
}
</script>
</head>
<body>
<input name="name" type="text" />
</body>
</html>
This test page includes the jsUnit library (jsUnitCore.js) which gives us testing functions like "assert". As with other unit testing frameworks, there are a slew of assert-variants, listed here.
5. Navigate to http://<web app>/jsUnit/testRunner.html (type in the URL to your web app in place of <web app>).
In the first text field, enter "<web app>/jsUnit_test.html"
Click the "Run" button
The bar on the page shows green, indicating that the test passed. (Note that tests are auto-discovered, so the function name must begin with "test".)
Architectural considerations
One thing that strikes me as sub-optimal about jsUnit is that your tests must be DIRECTLY inside your (HTML) test page. It would be nice if you could specify your test page in one test box (on the test runner page) and specify a Javascript file (which contains the actual tests) in another text box. The reason this would be handy is that you could specify the actual page that the Javascript will be used in (production code) as your test page without having to embed your Javascript tests in your production web page. Since that's not possible, here are some potential work-arounds:
A. Maintain a seperate test page from your regular (production) page
The problem with this approach is that if your Javascript depends on certain fields being present (or other UI constraints), then you will wind up needing to repeatedly migrate those fields (etc.) from your regular (production) page to your test page (or vise-versa if you're practicing TDD). The nice thing about this approach is that it forces you to think about the dependencies of your Javascript code on the HTML page that uses it. This could be adventageous when you want to re-use that Javascript because the test page provides a minimal template describing the UI elements that must be present in a page for it to use your Javascript library.
B. Include the unit tests in the regular (production) page
This is the most low-maintenance, low-tech solution to the problem. You don't have to migrate any UI fields or do anything fancy, but your pages will download slower because people that browse to your web page will be downloading your unit test code. This also might look funny to savvy users who do a 'view source' on your web page (or other developers maintaining your code in the future).
C. Server-side include mechanism
One solution that seems fairly slick to me would be to conditionally include the unit testing code in your regular (production) page using a server-side include that only comes into play when a query string parameter is present, like so:
<% if (Request["UnitTest"] == "true") { %>
<!-- #Include File="TestMyLibrary.js" -->
<% } %>
I have to admit that I haven't actually tried this approach so I'm not 100% sure it would work, but I'm confident that a solution along these lines would be possible through some mechanism. This solution would keep maintenance (field migration) to a minimum without affecting download performance.
D. Manual Discovery of unit tests
Another solution that is a little klunky in my opinion would be to force the discovery of unit tests in a seperate Javascript file by declaring a Javascript function called "exposeTestFunctionNames" in your regular (production) page that returns a list of strings, which are the names of each of your test methods. This solution would require you to include some test-related code (the aforementioned function) in your production code, but it would likely be far less than using solution B.
Decision time
Is there any better solutions that I haven't thought of? In the application that I'm currently building, the test page has to be seperate from the production web page anyways (the production web pages that use the Javascript we're developing will not be under our control), so option A is a no-brainer for my current situation. Since this is not a problem for me at the moment, I haven't actually tried to implement any of the aforementioned solutions, but I would be interested to hear how other people solve this problem (as I'm sure other people reading this blog post will be), so please post a comment!
Ooh... Where do we go now? (appologies to Guns N Roses)
Now that you have a basic unit test working with jsUnit, there are a number of resources that you may be interested in to help you kick it up a notch. This site lists a number of different Javascript tools that relate to unit testing. The most interesting thing I saw on there was jsmock, which is a mocking framework for Javascript. There is also JSCoverage, which generates code coverage statistics and JSXUnit which apparently is for unit testing managed JScript code, including code that runs in Silverlight. Last but certainly not least, you should go read the brief and to-the-point jsUnit documentation.