org.apache.cactus.extension.jsp
Class JspTagLifecycle

java.lang.Object
  extended by org.apache.cactus.extension.jsp.JspTagLifecycle

public final class JspTagLifecycle
extends java.lang.Object

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 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:
  1. Instantiation of the tag under test
  2. Instantiation of the lifecycle helper, passing in the page context and the tag instance
  3. Set the tag's attributes
  4. Start the tag's lifecycle by calling JspTagLifecycle.invoke()
  5. 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 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 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 Interceptor class, and adding it to the list of the tag lifecycles interceptors through 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 Also:
JspTestCase

Nested Class Summary
static class JspTagLifecycle.Interceptor
          Abstract class for intercepting the tag lifecycle.
 
Field Summary
protected  javax.servlet.jsp.PageContext pageContext
          The JSP page context.
 
Constructor Summary
JspTagLifecycle(javax.servlet.jsp.PageContext thePageContext, javax.servlet.jsp.tagext.Tag theTag)
          Constructor.
 
Method Summary
 void addInterceptor(JspTagLifecycle.Interceptor theInterceptor)
          Adds an interceptor to the interceptor chain.
 JspTagLifecycle addNestedTag(javax.servlet.jsp.tagext.Tag theNestedTag)
          Adds a nested tag.
 void addNestedText(java.lang.String theNestedText)
          Adds template text to nest inside the tag.
 void expectBodyEvaluated()
          Adds the expectation that the tag body must be evaluated once in the course of the tags lifecycle.
 void expectBodyEvaluated(int theNumIterations)
          Adds the expectation that the tag body must be evaluated a specific number of times in the course of the tags lifecycle.
 void expectBodySkipped()
          Adds the expectation that the tag body must be skipped.
 void expectScopedVariableExposed(java.lang.String theName, java.lang.Object[] theExpectedValues)
          Adds a special expectation that verifies that a specific scoped variable is exposed in the body of the tag.
 void expectScopedVariableExposed(java.lang.String theName, java.lang.Object[] theExpectedValues, int theScope)
          Adds a special expectation that verifies that a specific scoped variable is exposed in the body of the tag.
 void invoke()
          Invokes the tag with the provided interceptor.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

pageContext

protected javax.servlet.jsp.PageContext pageContext
The JSP page context.

Constructor Detail

JspTagLifecycle

public JspTagLifecycle(javax.servlet.jsp.PageContext thePageContext,
                       javax.servlet.jsp.tagext.Tag theTag)
Constructor.

Parameters:
thePageContext - The JSP page context
theTag - The JSP tag
Method Detail

addInterceptor

public void addInterceptor(JspTagLifecycle.Interceptor theInterceptor)
Adds an interceptor to the interceptor chain.

Parameters:
theInterceptor - The interceptor to add

addNestedTag

public JspTagLifecycle addNestedTag(javax.servlet.jsp.tagext.Tag theNestedTag)
Adds a nested tag. The tag will be invoked when the body content of the enclosing tag is evaluated.

Parameters:
theNestedTag - The tag to be nested
Returns:
The lifecycle wrapper for the nested tag, can be used to add expectations for the nested tag

addNestedText

public void addNestedText(java.lang.String theNestedText)
Adds template text to nest inside the tag. The text will be printed to the body content when it is evaluated.

Parameters:
theNestedText - The string containing the template text

expectBodyEvaluated

public void expectBodyEvaluated()
Adds the expectation that the tag body must be evaluated once in the course of the tags lifecycle.


expectBodyEvaluated

public void expectBodyEvaluated(int theNumIterations)
Adds the expectation that the tag body must be evaluated a specific number of times in the course of the tags lifecycle.

Parameters:
theNumIterations - The number of times the body is expected to get evaluated

expectBodySkipped

public void expectBodySkipped()
Adds the expectation that the tag body must be skipped. Essentially, this expectation verifies that the tag returns SKIP_BODY from doStartTag().


expectScopedVariableExposed

public void expectScopedVariableExposed(java.lang.String theName,
                                        java.lang.Object[] theExpectedValues)
Adds a special expectation that verifies that a specific scoped variable is exposed in the body of the tag.

Parameters:
theName - The name of the variable
theExpectedValues - An ordered list containing the expected values values of the scoped variable, one for each expected iteration step

expectScopedVariableExposed

public void expectScopedVariableExposed(java.lang.String theName,
                                        java.lang.Object[] theExpectedValues,
                                        int theScope)
Adds a special expectation that verifies that a specific scoped variable is exposed in the body of the tag.

Parameters:
theName - The name of the variable
theExpectedValues - An ordered list containing the expected values values of the scoped variable, one for each expected iteration step
theScope - The scope under which the variable is stored

invoke

public void invoke()
            throws javax.servlet.jsp.JspException,
                   java.io.IOException
Invokes the tag with the provided interceptor. The tag should have been populated with its properties before calling this method. The tag is not released after the tag's lifecycle is over.

Throws:
javax.servlet.jsp.JspException - If the tag throws an exception
java.io.IOException - If an error occurs when reading or writing the body content


Copyright © 2001-2009 The Apache Software Foundation. All Rights Reserved.