Convenience class that supports the testing of JSP tag by managing the tag's lifecycle as required by the JSP specification.
This class is basically a stub implementation of the tag management facilities that an actual JSP container would provide. The implementation attempts to follow the specification as closely as possible, but the tag handling functionality of real JSP implementations may vary in some details.
Although this class works quite well when used in the test methods of a {@link org.apache.cactus.JspTestCase JspTestCase}, it can also safely be used outside of the Cactus testing framework, for example when following a mock objects approach.
Testing Simple Tags
This is how you would use this class when testing the <c:set>
-tag of the JSTL reference implementation:
SetTag tag = new SetTag(); JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag); tag.setVar("name"); tag.setValue("value"); lifecycle.invoke(); assertEquals("value", pageContext.findAttribute("name"));
The order is important:
- Instantiation of the tag under test
- Instantiation of the lifecycle helper, passing in the page context and the tag instance
- Set the tag's attributes
- Start the tag's lifecycle by calling {@link #invoke() JspTagLifecycle.invoke()}
- Make assertions
Adding Expectations to the Lifecycle
JspTagLifecycle
features a couple of methods that let you easily add expectations about the tag's lifecycle to the test. For example, the method {@link #expectBodySkipped expectBodySkipped()} can be used to verify that tag's body is not evaluated under the conditions set up by the test:
IfTag tag = new IfTag(); JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag); tag.setTest("false"); lifecycle.expectBodySkipped(); lifecycle.invoke();
An example of a more sophisticated expectationion is the {@link #expectScopedVariableExposed(String,Object[])}method, which can verify that a specific scoped variable gets exposed in the body of the tag, and that the exposed variable has a specific value in each iteration step:
ForEachTag tag = new ForEachTag(); JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag); tag.setVar("item"); tag.setItems("One,Two,Three"); lifecycle.expectBodyEvaluated(3); lifecycle.expectScopedVariableExposed( "item", new Object[] {"One", "Two", "Three"}); lifecycle.invoke();
Custom Expectations
In some cases, using the expectations offered by JspTagLifecycle
does not suffice. In such cases, you need to use custom expectations. You can add custom expectations by creating a concrete subclass of the {@link JspTagLifecycle.Interceptor Interceptor}class, and adding it to the list of the tag lifecycles interceptors through {@link JspTagLifecycle#addInterceptor addInterceptor()}:
ForEachTag tag = new ForEachTag(); JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag); tag.setVarStatus("status"); tag.setBegin("0"); tag.setEnd("2"); lifecycle.addInterceptor(new JspTagLifecycle.Interceptor() { public void evalBody(int theIteration, BodyContent theBody) { LoopTagStatus status = (LoopTagStatus) pageContext.findAttribute("status"); assertNotNull(status); if (theIteration == 0) { assertTrue(status.isFirst()); assertFalse(status.isLast()); } else if (theIteration == 1) { assertFalse(status.isFirst()); assertFalse(status.isLast()); } else if (theIteration == 2) { assertFalse(status.isFirst()); assertTrue(status.isLast()); } } }); lifecycle.invoke();
Specifying Nested Content
JspTagLifecycle
let's you add nested tempate text as well as nested tags to the tag under test. The most important use of this feature is testing of collaboration between tags, but it also allows you to easily check whether a tag correctly handles its body content.
The following example demonstrates how to add nested template text to the tag, and how to assert that the body was written to the HTTP response on the client side:
public void testOutTagDefaultBody() throws JspException, IOException { OutTag tag = new OutTag(); JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag); tag.setValue(null); lifecycle.addNestedText("Default"); lifecycle.expectBodyEvaluated(); lifecycle.invoke(); } public void endOutTagDefaultBody(WebResponse theResponse) { String output = theResponse.getText(); assertEquals("Default", output); }
In sophisticated tag libraries, there will be many cases where tags need to collaborate with each other in some way. This is usually done by nesting such tags within eachother. JspTagLifecycle
supports such scenarios by allowing you to add nested tags to an existing tag lifecycle. The nested tags can than be decorated with expectations themselves, as you can see in the following example:
ChooseTag chooseTag = new ChooseTag(); JspTagLifecycle chooseLifecycle = new JspTagLifecycle(pageContext, chooseTag); WhenTag whenTag = new WhenTag(); JspTagLifecycle whenLifecycle = chooseLifecycle.addNestedTag(whenTag); whenTag.setTest("false"); whenLifecycle.expectBodySkipped(); OtherwiseTag otherwiseTag = new OtherwiseTag(); JspTagLifecycle otherwiseLifecycle = chooseLifecycle.addNestedTag(otherwiseTag); otherwiseLifecycle.expectBodyEvaluated(); chooseLifecycle.invoke();
The code above creates a constellation of tags equivalent to the following JSP fragment:
<c:choose> <c:when test='false'> <%-- body content not significant for the test --%> </c:when> <c:otherwise> <%-- body content not significant for the test --%> </c:otherwise> </c:choose>
@since Cactus 1.5
@version $Id: JspTagLifecycle.java 238991 2004-05-22 11:34:50Z vmassol $
@see org.apache.cactus.JspTestCase