Testing Auxiliary Tag Components

The definition of a custom tag extends beyond the behavior of the tag handler class. In order to test a custom tag thoroughly, we need test cases to verify that the tag library descriptor entry and/or the TagExtraInfo class perform as expected.

TagExtraInfo Classes

The tag element in the tag library descriptor file contains an optional <tei-class> subelement. The tei-class element contains the fully qualified name of a subclass of javax.servlet.jsp.tagext.TagExtraInfo. The TagExtraInfo class for a given tag provides two possible services: specifying scripting variables to be defined as part of the tag's execution (with getVariableInfo()) and providing translation-time validation of a tag's attributes (with isValid()). Both methods surrender to testing without complaint; in fact, TagExtraInfo tests do not need to be run in the container. Both methods depend on a single TagData object, which you can construct manually from a Hashtable or Object[][] (the TagData object represents name-value pairs for all of the attributes used in a given tag at translation time). Simply construct a TagData object and pass it to the method, then assert that the result is as expected.

Tag Library Descriptor Testing

Because deployment descriptors are declarative specifications of container behavior rather than executable program code, they evade most Java-based unit-testing techniques. However, creating a simple JSP that uses a tag in its intended manner provides a common-sense check that no major errors have crept in. Making this sanity-check JSP part of a unit-testing suite takes a small amount of effort and provides important test coverage. If you need to change the deployment descriptor to support a new feature, these JSP-based tests allow rapid discovery of any incompatibilities. Let's examine JSP-based descriptor tests by applying them to a common problem in tag testing: verifying the creation of a scripting variable. Scripting variables can be defined by a tag either in its TagExtraInfo class or in the tag's <variable> subelements in the tag library descriptor (in JSP 1.2). Both methods of defining a scripting variable rely on the container to generate the necessary servlet code. For example, the custom tag var specifies VarTei as its TagExtraInfo class in the deployment descriptor. According to the JUnit test, VarTei behaves correctly: specifying a single scripting variable var, to be newly created and available from the beginning of the tag onward. To test the container-generation process, we give the container a JSP that depends on the presence of var. Here is what such a page might look like:

<%@page import="junit.framework.Assert"%>
<%@taglib uri="WEB-INF/example.tld" prefix="example"%>
 Assert.assertEquals("foo", var);

If the container does not create var correctly, the attempt to access it in the scriptlet will cause a compilation error. So, this JSP constitutes a test. The value of the scripting variable (set in the VarTag handler class with pageContext.setAttribute("var", "foo")) is verified with assertions called directly from junit.framework.Assert. These assertions round out this sanity test, but we sense it's a bad idea to put too many assertions in the JSP-based test—it's probably better to use actual test cases because of the greater control they afford. To provide error logging and to make these hybrid tests part of the regular suite, we call the test JSPs from within ordinary Cactus tests. We begin by extending JspTestCase to provide access to the pageContext implicit object. Then, we implement a test method that calls pageContext.include() with an argument specifying the JSP test we want to run:

public void testVarCreate() throws Exception{
 pageContext.include("/var_create.jsp"); }

Any uncaught exceptions (including compilation exceptions!) are propagated up the stack to the test method where they are trapped and logged by the Cactus error handler.


This method of verifying the tag library descriptor may be useful, but it may also possess some problems. We don't guarantee perfect results, and you may need to pursue a variety of strategies to ensure that the container does not use older JSP translations. (For instance, occasionally you need restart some versions of Resin to ensure that it will retranslate JSPs.) That being said, we feel more comfortable with a unit test verifying that our code works as intended—and creating a sample JSP that illustrates possible tag configurations fulfills one of the most important functions of automated tests: serving as executable documentation.