JSP custom tags allow developers to abstract complex code out of a JSP page and into reusable components. JSP custom tags are not, however, standalone classes, and therefore cannot be fully tested with tools like JUnit. Once you think of custom tags as components in their own right, you see that they should be tested -- and tested as components. This pair of articles will introduce TagUnit -- an easy-to-use, open source testing tool that makes it possible to comprehensively test JSP tags.
Unit Testing and J2EE
Unit testing is the process of testing a particular piece of code by isolating that code and concentrating your tests on the expected operation of that code. Particularly with automated tools, unit testing gives you not only a level of confidence that your code works as expected, but also gives you a way to regression test your code when changes are introduced tomorrow, next week, or in years to come. This is very important for code stability and the freedom to refactor as necessary. As developers, unit-testing efforts are focussed at the method, class, or component level.
The unit-testing tool of choice for most Java developers is JUnit, which is a very simple testing framework that allows you to build up test cases consisting of assertions. These test cases typically focus on a specific class and can be brought together to form a test suite that might cover anything from a logical grouping of classes to your entire application. While JUnit is pretty much the de facto testing framework, the inherent complexities in building J2EE applications that execute inside of a J2EE application server pose new problems. For example, it's much harder to test components such as Enterprise JavaBeans or Java servlets without all of the plumbing and infrastructure that J2EE provides us as developers.
Thankfully, other tools and frameworks have been created to address some of these issues. As an example, we have Cactus, which provides a way to run JUnit tests inside of an application server; several mock-object libraries (for example, MockObjects) to help us stub out dependencies that our J2EE code has upon the underlying application server; and a few functional testing tools such as HttpUnit that help us to prove the function of the system from a black-box perspective. In effect, with a combination of these tools, it's possible to achieve a fairly high degree of code coverage in your testing. However, one area in which people struggle or forget to test is that of JSP custom tags.
Unit Testing JSP Custom Tags
Put simply, custom tags are a way of wrapping up recurring presentation logic into reusable components. If you look at a tag library such as the JavaServer Pages Standard Tag Library (JSTL), it has a number of useful tags that simplify the process of writing JSP pages. It has tags that iterate over collections and arrays, tags that output information from JavaBeans, tags that format dates, and so on. In addition to using pre-packaged tags, you may find yourself writing some of your own, or extending the functionality provided by libraries such as JSTL. In either case, how would you test that they function as expected?
One solution is to use JUnit and test the tag-handler classes as if they were standalone Java classes, testing each and every method in isolation. The problem with this approach is that custom tags are designed to be executed inside of a J2EE web container and ultimately have dependencies on classes such as
JspWriter, the implementations of which are provided by the JSP container vendor. This is not a new problem, and a common solution is to use one of the many mock-object frameworks to satisfy these dependencies by using dummy (mock) implementations. Mock objects work well but don't test the interaction between your code and a real implementation. An alternative approach is to use Cactus, a framework that allows you to build and execute tests inside of a real J2EE web container so that your code interacts with real objects. All of these solutions will work, and you may even be able to achieve 100 percent code coverage from your testing, but there is one point that we've not yet considered.
JSP Custom Tags Aren't Standalone Classes
Custom tags are more than standalone Java classes; they are components in their own right. Alongside each tag handler class is a tag definition inside of a tag library descriptor (TLD) file that defines the name of each tag, the attributes it can take, what type of body content is permitted, etc. At development time, you use custom tags inside of your JSPs through an XML syntax and not by directly instantiating the tag handler classes. For all intents and purposes, this XML syntax defines the component interface to any given tag. Because tags are used in this way, it makes most sense to test them in this way, too. In other words, custom tags are components and therefore need to be tested at that level, in the way that they would normally be used from within a JSP page.
Having established that custom tags are components, it now starts to become clear why you might want to test them as components rather than as standalone classes. Imagine that someone in your team has written a service (something like an EJB or web service) and told you that he or she has only tested the implementation, without using the public interface. While this might give you some confidence, you'd probably like to know that you can successfully use the implementation via its API. In addition to this, the people that will use your tags will often try to use them in weird and wonderful ways that you had never imagined, so some degree of testing is certainly beneficial, particularly if you are building and releasing tags for use by other teams.
A final reason why it's good to test custom tags as components is because of the often complex interactions that they have with the web container. A great deal of work has gone into JSP 2.0 around simplifying the custom tag programming model, although the actual lifecycle of tags is still frequently misunderstood, particularly around optional features of the JSP specification such as tag handler instance pooling and cleaning up state between invocations. Unfortunately for us, each J2EE server vendor has a slightly different approach to implementing the specification and this can make it harder to produce truly portable tag handler implementations. Having an in-container test suite makes this particularly easy to assert.
Unit Testing JSP Custom Tags with TagUnit
Now that we've looked at why we should test custom tags as components, we need to look at how this is possible. Earlier on, we briefly discussed some of the options that are available for testing custom tags, namely with tools such as JUnit, Cactus, and mock objects. Another possible solution that wasn't mentioned is that you could build a JSP-based test harness that contained a number of tags and use something like HttpUnit to perform assertions that the output generated is as expected. Unfortunately, this doesn't take into account the fact that some tags don't produce HTML.
The motivation behind TagUnit is to treat tags as the units from which JSP pages are built and to provide a way in which they can be tested in isolation from the application-specific pages on which they will ultimately be used. Since tags are dependent on the features provided by the JSP container in which they run, it makes sense to test them from within the JSP container. This approach provides a number of benefits.
Although the individual tag handler methods can be tested as a part of the unit-testing process, this still doesn't guarantee that they will work inside of the container when deployed. Testing tags inside of a container addresses this risk up front.
Custom tags may have several side effects that are much more easily tested when they are running inside of a container. These include the introduction of scripting variables, manipulation of page-context attributes, iteration and manipulation of body content, and so on.
Having a suite of in-container tests means that you can take those tests and run them inside of another container to check that you aren't relying on container-specific features or subtleties in the specific vendor's implementation of the JSP specification. Of course, on the flip side, it does mean that you can test such features if you need to make use of them.
So then, what exactly is TagUnit? Essentially, it's a web application consisting of a number of JSPs and tag libraries that you can use to test your own tag libraries. In the same way that JUnit lets you build up test cases containing assertions for standalone Java classes, TagUnit allows you to do the same for testing tag libraries. TagUnit provides a number of assertion tags, and these are used to perform the actual tests, with multiple JSP pages being used to build up the test cases.
For the purpose of this article, let's say that we would like to test the core taglib from the Jakarta Taglibs implementation of the JSTL, a taglib that many people will be aware of and have experience with. Assuming that you already have a container compatible with JSP 1.2/Servlet 2.3, such as Tomcat 4.1.x, the following steps describe how to get started with TagUnit. Throughout the instructions, we'll assume that you're using a default Tomcat install under
$TOMCAT_HOME and running on the standard port of 8080. The steps to getting TagUnit up and running are as follows.
Inside of the TagUnit distribution is a template web application called tagunit-blank.war. Because we're going to add to this webapp later, you'll need to deploy it to your JSP container in the exploded format. If you're using Tomcat, simply create a directory called tagunit-blank underneath
$TOMCAT_HOME/webappsand extract the contents of the .war file into this directory.
At this point, if you would like to test the deployment, restart your server and point your browser to http://localhost:8080/tagunit-blank/. If all is well, you will see a page containing a number of test results for the TagUnit tags themselves.
The next step is to deploy your tag library into thetagunit-blank web application. With most tag libraries, this just involves copying one or more .jar files intoWEB-INF/lib. With JSTL, you'll need to copystandard.jar and jstl.jar from the libdirectory of the JSTL distribution into the WEB-INF/libdirectory of the web application.
With the tags deployed, we now need to tell TagUnit about them. To do this, open up the /test/index.jsp file from the web application into your favorite text editor and modify the content to be the following.
<%@ taglib uri="http://www.tagunit.org/tagunit/core" prefix="tagunit" %> <tagunit:testTagLibrary uri="/test/jstl-core"> <tagunit:tagLibraryDescriptor jar="standard.jar" name="c.tld"/> </tagunit:testTagLibrary>
The first line imports the TagUnit tags, making them available for use, while the final three tell TagUnit that our tests will reside underneath /test/jstl-core and that we're testing the tag library deployed in [WEB-INF/lib/]standard.jar, described by the TLD file called c.tld inside of that .jar file.
Although we've not even started writing tests to exercise the tags yet, we are already in a position to let TagUnit perform some preliminary testing of its own, simply based upon the fact that TagUnit has been told about a tag library. If you restart the server at this point and point your browser to http://localhost:8080/tagunit-blank/, you will see some test results for the JSTL core tag library, as shown in Figure 1.
The block of color at the top of the page gives a quick indication about the status of the tests -- green for all passes, orange for warnings, and red for failures/errors. For the moment, don't be alarmed that we have several warnings. This is just TagUnit's way of telling you that you've not actually written any tests yet. If you scroll down the page, you should see that each and every tag in the JSTL core tag library is listed. Each of these has its own test status and nested underneath are the tests that have been executed. By default, TagUnit runs a set of automatic tests against each custom tag in the tag library based upon the information provided by the TLD file. These tests assert that:
- The tag handler class is loadable from the classpath, can be instantiated and implements the basic
- As above but with the
javax.servlet.jsp.tagext.TagExtraInfoclass, if applicable.
- A getter method is available for each tag attribute and the parameter type is loadable from the classpath.
Even if you were to never actually write a test with TagUnit, these automatic tests are useful in their own right, giving you confidence that you've not forgotten to implement any of the basic tag handler contract; for example, helping to ensure that the attributes on the tag handler match those that are defined in the TLD file. Additionally, they help prevent situations where tags are packaged and/or deployed incorrectly.
One of the conclusions that many developers come to when building J2EE applications is that a single testing tool doesn't always provide the ability to perform testing to cover the complete codebase. Tools like JUnit certainly allow you to unit test a high percentage of the codebase, but J2EE applications are, by nature, constructed of multiple higher-level components. This article introduced TagUnit and showed why and how you should consider unit testing your custom tags. TagUnit complements other testing tools and can be thought of as another tool in your toolbox on your quest to thoroughly test your J2EE applications. In the next article, we'll look at how to build up a test suite with TagUnit to test the functionality provided by custom tags.