![]() | ![]() |
Apache > Jakarta > Cactus > Writing Tests | Docs for: v1.7.2 | v1.7 Last update: March 29 2013 |
When to use?
Your test case class should extend
This tutorial focuses on testing custom tags, as they are the principal code which uses the JSP API objects. Future versions of this tutorial will expand upon testing actual JSPs. Overview of Tag Library TestingCustom tags consist of entries in a Tag Library Descriptor file (TLD) and a tag handler class. Cactus provides the facility to test both aspects of a custom tag. However, since the TLD contains no logic, you will use Cactus primarily to test the tag handler class.
To test the tag handler class, use the implicit objects provided by
JspTestCase to set up the initial state for the test. Then create
and initialize your custom tag using the For an additional degree of integration testing, you can create a JSP that exercises your custom tag and call it from within a regular Cactus test case. See the section on Further Integration Testing for details.
To simplify the complexities of managing a tag handlers lifecycle,
Cactus provides the helper class
JspTagLifecycle . It
is basically a stub implementation of a JSP engines tag management
routines, and provides many convenient shortcut methods to test
tag handlers. For detailled documentation, check out the
corresponding
API documentation.
Currently, JspTagLifecycle is only available for
JSP 1.2.
Provided Implicit Objects
Cactus automatically initializes the following implicit objects.
They are made available to your
See the How it
works guide for details on how Cactus initializes these objects.
The provided implicit objects are: request
See response
See config
See session
See out
Cactus does not wrap the out object.
You can use this object to write data to the response, thereby
simulating the body of a tag (if the tag does not modify its body). If
the tag does modify its body, then you will need to
generate a BodyContent object before writing out the simualted body.
See pageContext
Custom tags rely exclusively on the
See the javadoc for
bodyContent
JspTestCase does not actually provide a
It's important to balance
calls to
pushBody() with calls to
popBody() --otherwise many servlet engines will not
output the tag's body. The easiest way to accomplish this is to
call pushBody in setUp() and popBody()
in tearDown() .
Custom Tag Set Up
Creating the test fixture for a custom tag test involves several
steps. The exact order of the steps can vary depending on the
needs of the test. For instance, placing the test data in the
correct scope would probably happen before a real JSP began its
execution. You can emulate this, or choose to do it after the
tag has been in initialized (as described below). In most cases
you can determine the exact order of the steps based on what
is most convenient for a given test (some steps may be specific
to only one test in the Step 1: Create the Tag (Required)Instantiate a copy of the tag you wish to test. SomeTag tag = new SomeTag(); Step 2: Set the pageContext (Optional)
Call the tag.setPageContext(pageContext); Step 3: Set the tag's attributes (Optional)If your tag takes attributes, call setter methods to initialize the tag's state. Setters on the tag handler class represent the attributes of custom tags. Thus to emulate this JSP fragment: <someTag foo="10" bar="11"/> You would need to use the following: someTag.setFoo("10"); someTag.setBar("11"); Step 4: Set the parent tag (Optional)If you would like the tag you are testing to access a parent tag, you will need to call tag.setParent(enclosingTag);
This will allow tag to successfully call Step 5: Create the BodyContent Object (Optional)
If your tag processes its body, call
Step 6: Set up page state (Optional)
Set up any necessary page state by putting test objects into the
appropriate scopes. Tags frequently access data in the
session, the request, or the page. If your tag operates on data
contained in any of these (or in the application scope), be sure
to set up this part of the test fixture. Objects can be placed
in these scopes by using the implicit objects provided by Cactus
directly, or by accessing them indirectly through the
request.setAttribute("key", new DomainObject("testValue")); //or pageContext.setAttribute("key", new DomainObject("testValue"), PageContext.REQUEST_SCOPE); Running the TestOnce the tag has been set up and any necessary page data has been placed in the appropriate scopes, testing a custom tag consists of calling the relevant life-cycle methods and then using JUnit asserts to verify the outcome. Verifying individual methods
Most of the life cycle methods return tag.setValueThatResultsInInclude("correct value"); assertEquals(Tag.EVAL_BODY_INCLUDE, tag.doStartTag()); Checking effects on page data
In addition to "listening" for the signals that your tag sends to
the container, you may want to verify that the tag's execution
has the appropriate effects upon the page data. Use
catalogListTag.doStartTag(); Collection catalogs = (Collection)request.getAttribute("catalogs"); assertNotNull(catalogs); Verifying tag output
Use the
This example uses the
endXXX()
signature from Cactus 1.2 or above.
public void endSomeTagTest (WebResponse response) { String output = response.getText(); assertEquals("<b>expected output</b>", output); } Special CasesThere are a few scenarios in custom tag testing that deserve extra attention. Iteration Tags
To test a tag that repeats its body processing a number of
times, simply create a //[...tag set up and early life cycle methods omitted...] int count = 0; do { count++; } while (tag.doAfterBody() == tag.EVAL_BODY_AGAIN); tag.doEndTag(); //based on setUp we expect 9 repetitions assertEquals(9, count); You can use a count variable (such as the one illustrated in the example) to check whether the tag's body was processed the expected number of times. Body Tags
Unless specified otherwise by the deployment descriptor, all
tags can include a body, which can in turn include other tags
or scriptlet expressions. These are automatically evaluated at
run time, and the content of the body is simply written out
if the tag signals it should be (with
Testing BodyTags--tags which actually perform some processing
on their content--is a little trickier.
BodyTags can choose to return a constant (
To test body tags, your test must replicate this somewhat complicated lifecycle. The following code covers all of the steps as they might appear in a typical test: //standard set up YourTag tag = new YourTag(); tag.setPageContext(this.pageContext); tag.doStartTag(); //obtain the bodyContent object--presumably doStartTag has returned //EVAL_BODY_TAG or EVAL_BODY_BUFFERED. BodyContent bodyContent = this.pageContext.pushBody(); this.tag.setBodyContent(bodyContent); this.tag.doInitBody(); //write some "output" into the bodyContent so that endXXX can test for it. bodyContent.println("Some content"); bodyContent.print("Some evaluated content " + (5 + 9)); //actually handles the processing of the body tag.doAfterBody(); //after the body processing completes tag.doEndTag(); //finally call popBody this.pageContext.popBody();
This sample does not fully replicate the container's handling of
the tag (for instance, the tag would only receive the
Again, you can check that the body of the tag was handled
correctly by verifying the total output in the
endXXX() method.
TagExtraInfo classes
Cactus does not offer any specific services to support the
testing of Further Integration TestingYou can use Cactus to test how your tag will react when put into a real JSP. This allows you to verify that there are no problems with the deployment descriptor, or unexpected behavior on the part of the container. You accomplish this by writing a small JSP that makes use of your custom tag, and then calling it from within a Cactus test case. You can even use JUnit assertions within scriptlets to verify certain aspects of the Tag's behavior. However, this method requires that you write a separate JSP for each test case (or lump several cases into a single JSP). Both options pose problems, so it may be best to include one or two tests of this type and rely on the more traditional methods described earlier to ensure total coverage. The test JSP
All the JSP needs to do is include the tag library that
describes the tag you are testing and makes use of it in
some way. You can import <%@page import="junit.framework.Assert"%> <%@taglib uri="WEB-INF/yourTagLib.tld" prefix="example"%> Here is the custom tag this page verifies: <example:someTag variableName="foo" variableValue="bar"/> Here is the JUnit assert that checks whether the tag correctly created a scripting variable named <code>foo</code> with the value "bar": <% //attempt to reference foo will cause a translation error if the tag did not //create the scripting variable Assert.assertEquals("bar", foo); %>
It's a bad idea to put too many assertions into the
JSP. In the example above, the creation of a scripting variable
can only be tested within the JSP page. (The
same goes for any objects in page scope, because each JSP
creates its own.) If you
want to use other assertions with this type of test,
call them in your test case after
The TestCase
To use the test JSP, include it from within a
public void testSomeTag () throws Exception { pageContext.include("/test_some_tag.jsp"); //an assert to check whether the page also mapped foo into the session assert("bar", session.getAttribute("foo")); } Exceptions that result from either page translation (such as required attributes being omitted, or the tag missing a part of its descriptor entry) or page execution (such as the tag being unable to find required data in the appropriate scope) are automatically be thrown up to this level. If you do not catch them there they will be logged by Cactus/JUnit as failures--which is just what you want.
Any output that the test JSP generates can be checked normally
in the
endXXX method.
Of course, using this strategy means that you need to put
the |