Introduction to Tag Unit, Part 2 Blog



    Test Suites, Test Cases, and Assertions
    Testing the JSTL forEach Tag
    Testing the Interface
    Testing Fixed Loops
    Other Features

    In Introduction to TagUnit: Part 1, we looked at some of the various ways in which custom tags can be tested before demonstrating some of the basic features provided by TagUnit--an open source testing tool specifically aimed at JSP custom tags. In this article we'll complete the story by looking at some examples of how tests can be written with TagUnit.

    Test Suites, Test Cases, and Assertions

    If you have experience with other testing frameworks, the conventions and frameworks of TagUnit will feel familiar. In JUnit we have test suites, test cases, and assertions. Likewise in TagUnit. Tests are written as assertions and these tests are grouped together to form a larger test case. Test cases are then assembled to form a test suite. The key difference between TagUnit and most other testing tools is that tests within TagUnit are written as JSP pages rather than Java classes. In TagUnit, tests are broken down as follows.

    • An assertion is written as a custom tag.
    • A test consists of one or more assertions, written inside a JSP page.
    • A test case consists of many tests and is a grouping of all the tests for a given custom tag.
    • A test suite is the collection of all test cases for a given tag library.

    With this basic understanding of the test structure we can now go on and write some tests.

    Testing the JSTL forEach Tag

    Assuming that you followed the setup instructions in the previous article, you should be able to point your browser to http://localhost:8080/tagunit-blank/and see the results for the automatic tests that are performed on the JSTL core tag library. To illustrate how to use TagUnit, let's write a few tests for the JSTL forEach tag that provides iteration facilities over a number of different object types, including collections and arrays.

    The first thing we need to do is create the directories in which the tests will reside. The way TagUnit works is that there is a directory-per-tag library being tested (a test suite) and subdirectories underneath these for each tag (a test case) that is being tested. In the index.jsp page we introduced in the first article, we used the following piece of code to tell TagUnit which tag library we would like to test.

    <%@ taglib uri="" 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. 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 that JAR file. The uri attribute of thetestTagLibrary tag describes a path underneath this web application where the tests for the JSTL core tag library will be written. You need to create this directory and a further subdirectory for each tag that you would like to test. In this case, create a subdirectory called forEach to hold the tests for the forEach tag. The resulting web application structure is shown in figure 1.

    Figure 1. Web application structure
    Figure 1. Web application structure

    With the directory structure in place we can write some tests. As mentioned earlier, assertions are custom tags and a test consists of one or more assertions, written as a JSP page. To follow the JUnit convention, these JSPs must be namedtestX.jsp, where X can be any arbitrary string. Therefore, in the same way that JUnit lets you have more than one test inside a test case by defining multipletestX() methods, TagUnit lets you define one or moretestX.jsp pages so that tests can be grouped and/or split as necessary.

    Testing the Interface

    Many of the current crop of IDEs are able to perform JSP validation either incrementally or during the build process. From a JSP tag perspective, this is a very useful feature in that it lets you know when you've used the wrong set of attributes for a given tag, or specified body content for an empty tag, and so on. More commonly, a developer will only discover that a tag has been used incorrectly at runtime, when the page is requested. During the development cycle, another potential problem with using tags is that another member of the team might change the tag definition that includes the set of attributes and body content. In the first article, we said that the set of attributes and body content can be thought of as the interface of the tag. When you change a Java interface, you will generally expose this type of error at compile time. This generally isn't the case with JSP tags. One way to address this problem is to write a test that performs assertions based upon the interface, or specification, of that tag. With TagUnit this is straightforward.

    The first step in writing a TagUnit test is to create a JSP page underneath the appropriate directory that follows the previously mentioned naming standard. Since we're going to be testing the specification of the forEach tag, we'll create a JSP page called testSpecification.jsp underneath theforEach directory. We'll be using the TagUnit tags to perform assertions so the following taglib directive should be placed at the top of the page.

    <%@ taglib uri="" prefix="tagunit" %>

    Next are the assertions.

    <tagunit:assertBodyContent name="JSP"/> <tagunit:assertAttribute name="var" required="false" rtexprvalue="false"/> <tagunit:assertAttribute name="items" required="false" rtexprvalue="false"/> <tagunit:assertAttribute name="varStatus" required="false" rtexprvalue="false"/> <tagunit:assertAttribute name="begin" required="false" rtexprvalue="false"/> <tagunit:assertAttribute name="end" required="false" rtexprvalue="false"/> <tagunit:assertAttribute name="step" required="false" rtexprvalue="false"/> <tagunit:assertInterface name="javax.servlet.jsp.jstl.core.LoopTagSupport"/>

    The TagUnit documentation contains a full reference of the assertions available, but here we'll briefly explain their purpose. The first tag (assertBodyContent) performs an assertion based upon the body content of the tag as defined in the TLD file, checking that the value specified matches. The second tag (assertAttribute) tests that the given attribute is defined for the tag and, optionally, has the specified characteristics. In this example, we perform assertions for the entire set of attributes for the forEach tag, asserting that they are not required and don't accept runtime expressions. Although not the case here, theassertNoAttributes tag can be used to test that the custom tag under test doesn't declare any attributes in the TLD file. The final tag (assertInterface) tests that the tag handler implements the given (fully qualified) class or interface name. For tags that allow extension and/or cooperation, this is an important part of the contract because other tags generally rely on casting tag handler instances at runtime, something that's hard to pick up during a standard build cycle. Writing a test allows you to see when a tag handler stops implementing the given interface.

    Testing Fixed Loops

    The forEach tag has the ability to iterate over a number of different types of collections and arrays, while also being able to provide a fixed number of iterations, like a traditional for loop, by specifying thebegin and end attributes. Optionally, thevar attribute allows us to specify the name of a variable that will be exposed and continuously updated with the next item in the iteration. The following example shows a fixed loop that will output 123 (we're using the JSTL coreout tag to output the the value ofi).

    <c:forEach var="i" begin="1" end="3"> <c:out value="${i}"/> </c:forEach>

    So, if we were the developer of this tag, how would we test this?

    In addition to the assertions that we've already seen, TagUnit provides other assertions that can be used to compare output and the availability of scoped objects. To start with, let's create a JSP called testFixedLoop.jsp underneath theforEach directory again and start off by including the various taglib directives that we will need for our tests.

    <%@ taglib uri="" prefix="tagunit" %> <%@ taglib prefix="c" uri="" %>

    Since we're going to be using the JSTL core tags, we need to import these too this time around. Next, let's write a simple test for the forEach tag that proves that it can correctly perform three iterations.

    <tagunit:assertEquals name="Fixed loop with static body content"> <tagunit:expectedResult>TestTestTest</tagunit: expectedResult> <tagunit:actualResult> <c:forEach begin="1" end="3">Test</c:forEach> </tagunit:actualResult> </tagunit:assertEquals>

    In this example we're using the assertEquals tag to indicate that we would like to perform a comparison. As with JUnit, we have an expected result and an actual result, which are wrapped up between the expectedResult andactualResult tags, respectively. At runtime, the TagUnit tags consume any content that is output within their body content to perform the comparison. That's all there is to building simple tests, and although the XML can seem a little verbose the semantics of the tests are similar to those written with JUnit.

    Next, to show something a little different, let's write a test for the var attribute that we mentioned earlier in a file called testVarAttributeExposed.jsp. In this test we'll create an array of strings and let the forEachtag iterate over them using the items attribute. By specifying the var attribute, the forEachtag will expose each element of the array in turn.

    <%@ taglib uri="" prefix="tagunit" %> <%@ taglib prefix="c" uri="" %> <% String strings[] = new String[] {"A string"}; pageContext.setAttribute("strings", strings); %> <c:forEach var="item" items="${strings}"> <tagunit:assertPageContextAttribute name="item" type="java.lang.String" value="A string"/> </c:forEach>

    Here the assertPageContextAttribute tag is being used to assert for the existence of a page scoped object with the given name, type, and value. Exposing objects is another common usage pattern for custom tags and something that is also worth testing.

    Other Features

    In addition to the assertEquals tag, TagUnit provides other comparison-based assertions, includingassertNotEquals, assertContains, andassertMatches. The latter two are useful when you are testing custom tags that output a large amount of content and only want to test for the presence of key information. In the previous example you probably noticed that we created an array of strings and placed these into the page scope. If this type of logic is common to many tests, we can write a JSP page calledsetUp.jsp in the same way that we can write asetUp() method in a JUnit testcase. Similarly, we can write a page called tearDown.jsp. Some final points to mention are that TagUnit has integration with Ant so that TagUnit tests can be executed automatically, as part of a larger build process and it's easy to get code coverage information for your custom tags with tools like Clover.


    Custom tags typically encapsulate common presentation logic on JSP pages and generally have some effect on how the pages themselves are rendered. Some tags output markup while some retrieve information, ready for it to be displayed by other components. Without a complete JSP environment, testing tags is difficult and this is the primary reason that TagUnit was created. Using the tool we can easily build up test suites for JSP tags that can be deployed and executed in one or more JSP containers with ease, which is also useful for testing the portability of tags between vendor implementations.

    Following on from the first article, this article shows how to start building tests with TagUnit. Tests are written as JSP pages and by using the various assertion tags, complex test cases and test suites can be built up. In conjunction with other testing tools, TagUnit provides a way to help test more of your J2EE web applications. With TagUnit, custom tags can be tested in the same way that page authors will use them--from JSP pages.