vmassol     01/09/18 10:25:13

  Modified:    docs/framework/xdocs howto_testcase_jsp.xml
  Log:
  taglib tutorial provided by Nicholas Lesiecki
  
  Revision  Changes    Path
  1.2       +568 -4    jakarta-cactus/docs/framework/xdocs/howto_testcase_jsp.xml
  
  Index: howto_testcase_jsp.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-cactus/docs/framework/xdocs/howto_testcase_jsp.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- howto_testcase_jsp.xml    2001/09/11 14:33:10     1.1
  +++ howto_testcase_jsp.xml    2001/09/18 17:25:13     1.2
  @@ -6,7 +6,7 @@
     <header>
       <title>JspTestCase Principles</title>
       <authors>
  -      <person name="Vincent Massol" email="[EMAIL PROTECTED]"/>
  +      <person name="Nicholas Lesiecki" email="[EMAIL PROTECTED]"/>
       </authors>
     </header>
   
  @@ -20,24 +20,588 @@
         </p>
         <ul>
           <li>
  -          Tag libraries,
  +          Custom tags,
           </li>
           <li>
             Any java code that uses JSP API objects
             (<code>PageContext</code>, ...)
           </li>
         </ul>
  +      <p>
  +        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.
  +      </p>
  +    </s1>
   
  +    <s1 title="Overview of Tag Library Testing">
  +        <p>
  +            Custom 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.
  +        </p>
  +        <p>
  +            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 <code>pageContext</code>
  +            implicit
  +            object. After setting up the tag, call the lifecycle methods 
  +            implemented by your tag in the correct order and verify that the 
  +            methods return the expected results. The tag's output can be 
  +            inspected in the <code>endXXX()</code> method.
  +        </p>
  +        <p>
  +            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 <jump 
  +            anchor="further_integration">Further Integration Testing</jump> 
  +            for details.
  +        </p>
       </s1>
   
       <s1 title="Provided Implicit Objects">
   
         <p>
  -        <strong>This tutorial is still being written and should be
  -        available shortly. We apologise for the inconvenience.</strong>
  +        Cactus automatically initializes the following implicit objects.
  +        They are made available to your <code>setUp()</code>,
  +        <code>testXXX()</code> and <code>tearDown()</code> methods as
  +        instance variables of the <code>JspTestCase</code> class (and thus
  +        as instance variables of your test case class).
         </p>
  +      <note>
  +        See the <link href="how_it_works.html">How it
  +        works</link> guide for details on how Cactus initializes these objects.
  +      </note>
  +
  +      <p>
  +        The provided implicit objects are :
  +      </p>
  +
  +      <anchor id="jsp_request"/>
  +      <s2 title="request">
  +
  +        <p>
  +         See <code>ServletTestCase</code><code>
  +         <link href="howto_testcase_servlet.html#servlet_request">request
  +         </link></code> implicit object for documentation.
  +       </p>
  +
  +      </s2>
  +
  +      <anchor id="jsp_response"/>
  +      <s2 title="response">
  +
  +        <p>
  +         See <code>ServletTestCase</code><code>
  +         <link href="howto_testcase_servlet.html#servlet_response">response
  +         </link></code> implicit object for documentation.
  +        </p>
  +
  +      </s2>
  +
  +      <anchor id="jsp_config"/>
  +      <s2 title="config">
  +
  +        <p>
  +         See <code>ServletTestCase</code><code>
  +         <link href="howto_testcase_servlet.html#servlet_config">config
  +         </link></code> implicit object for documentation.
  +        </p>
  +      </s2>
  +
  +      <anchor id="jsp_session"/>
  +      <s2 title="session">
  +
  +        <p>
  +         See <code>ServletTestCase</code><code>
  +         <link href="howto_testcase_servlet.html#servlet_session">session
  +         </link></code> implicit object for documentation.
  +        </p>
  +      </s2>
  +
  +      <anchor id="jsp_out"/>
  +      <s2 title="out">
  +
  +        <table>
  +          <tr>
  +            <td>
  +              Instance variable name
  +            </td>
  +            <td>
  +              <strong><code>out</code></strong>
  +            </td>
  +          </tr>
  +          <tr>
  +            <td>
  +              Class name
  +            </td>
  +            <td>
  +              <code>public javax.servlet.jsp.JspWriter</code>
  +            </td>
  +          </tr>
  +        </table>
  +
  +        <note>
  +          Cactus does not wrap the out object.
  +        </note>
  +        <p>
  +         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 <strong>does</strong> modify its body, then you will need to 
  +         generate a BodyContent object before writing out the simualted body.
  +         See <jump anchor="body_content"><code>bodyContent</code></jump> for
  +         details.
  +        </p>
  +      </s2>
  +
  +      <anchor id="jsp_pageContext"/>
  +      <s2 title="pageContext">
  +
  +        <table>
  +          <tr>
  +            <td>
  +              Instance variable name
  +            </td>
  +            <td>
  +              <strong><code>pageContext</code></strong>
  +            </td>
  +          </tr>
  +          <tr>
  +            <td>
  +              Class name
  +            </td>
  +            <td>
  +              <code>org.apache.cactus.server.PageContextWrapper
  +              </code>, which inherits from
  +              <code>javax.servlet.jsp.PageContext</code>
  +            </td>
  +          </tr>
  +        </table>
  +
  +        <p>
  +          Custom tags rely exclusively on the <code>pageContext</code> object to
  +          provide information about the enclosing JSP. Therefore this is the 
  +          most important implicit object for testing custom tags. Cactus 
  +          provides a very thin wrapper that ensures that all of the objects that 
  +          <code>pageContext</code> returns (such as the <code>ServletRequest
  +          </code> from <code>pageContext.getRequest()</code>) are the correctly
  +          wrapped versions available in the other implicit variables.
  +        </p>
  +        <p>
  +          See the javadoc for
  +          <code>org.apache.cactus.server.PageContextWrapper</code>
  +          for more details. You should also look at the
  +          samples provided in the Cactus distribution.
  +        </p>
  +      </s2>
  +
  +
  +    <anchor id="jsp_bodyContent"/>
  +      <s2 title="bodyContent">
  +
  +        <table>
  +          <tr>
  +            <td>
  +              Instance variable name
  +            </td>
  +            <td>
  +              <strong><code>bodyContent</code></strong>
  +            </td>
  +          </tr>
  +          <tr>
  +            <td>
  +              Class name
  +            </td>
  +            <td>
  +              <code>javax.servlet.jsp.tagext.BodyContent</code>
  +            </td>
  +          </tr>
  +        </table>
  +
  +        <p>
  +          JspTestCase does not actually provide a <code>bodyContent</code> 
  +          implicit object for use with <code>BodyTags</code>. However, obtaining
  +          one is so easy that it deserves mention here. Calling <code>
  +          pageContext.pushBody()</code> returns an object of type <code>
  +          javax.servlet.jsp.tagext.BodyContent</code> (which inherits from 
  +          <code>JspWriter</code>). This call also changes the value of the 
  +          "out" variable stored in page scope (and thus the value of 
  +          <code>pageContext.getOut()</code>). To put test content into the 
  +          <code>bodyContent</code> object, simply use its writer methods. To
  +          quote Sun's API reference on the matter: "Note that the content of
  +          BodyContent is the result of evaluation, so it will not contain
  +          actions and the like, but the result of their invocation." 
  +          See <jump anchor="using_body_content">Body Tags</jump> 
  +          for more information.
  +        </p>
  +        
  +      </s2>
  +    </s1>
  +
  +    <s1 title="Custom Tag Set Up">
  +        <p>
  +            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 <code>TestCase</code> and so should 
  +            be executed after common <code>setUp()</code> code).
  +        </p>
  +        <s2 title="Step 1: Create the Tag (Required)">
  +            <p>
  +                Instantiate a copy of the tag you wish to test.
  +            </p>
  +<source><![CDATA[SomeTag tag = new SomeTag();]]></source>
  +        </s2>
  +
  +        <s2 title="Step 2: Set the pageContext (Optional)">
  +            <p>
  +                Call the <code>setPageContext()</code> method with the implicit
  +                object provided by Cactus to register the pageContext with the
  +                tag.
  +            </p>
  +<source><![CDATA[tag.setPageContext(pageContext);]]></source>
  +        </s2>
  +
  +        <s2 title="Step 3: Set the tag's attributes (Optional)">
  +            <p>
  +                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:
  +            </p>
  +<source><![CDATA[<someTag foo="10" bar="11"/>]]></source>
  +            <p>
  +                You would need to use the following:
  +            </p>
  +<source><![CDATA[
  +someTag.setFoo("10");
  +someTag.setBar("11");
  +]]></source>
  +
  +        </s2>
  +
  +        <s2 title="Step 4: Set the parent tag (Optional)">
  +            <p>
  +                If you would like the tag you are testing to access a parent 
  +                tag, you will need to call 
  +            </p>
  +<source><![CDATA[tag.setParent(enclosingTag);]]></source>
  +            <p>
  +                This will allow tag to successfully call <code>getParent</code>
  +                and <code>TagSupport.findAncestorWithClass()</code>. Of course 
  +                <code>enclosingTag</code> will have to be instantiated and 
  +                set up as well, including another call to 
  +                <code>setParent()</code> if you would like to simulate multiple
  +                levels of nesting.
  +            </p>
  +        </s2>
  +    
  +
  +        <s2 title="Step 5: Create the BodyContent Object (Optional)">
  +            <p>
  +                If your tag processes its body, call 
  +                <code>pageContext.pushBody()</code> 
  +                to obtain a <code>BodyContent</code>. See the 
  +                <jump anchor="using_body_content">Body Tags</jump> section
  +                for more details.
  +            </p>
  +        </s2>
  +
  +
  +        <s2 title="Step 6: Set up page state (Optional)">
  +            <p>
  +                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 
  +                <code>pageContext</code> object.
  +            </p>
  +<source><![CDATA[
  +request.setAttribute("key", new DomainObject("testValue"));
  +//or
  +pageContext.setAttribute("key", new DomainObject("testValue"), 
PageContext.REQUEST_SCOPE);
  +]]></source>
  +        </s2>
  +    </s1>
  +
  +    <s1 title="Running the Test">
  +        <p>
  +            Once 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.
  +        </p>
  +        <s2 title="Verifying individual methods">
  +            <p>
  +                Most of the life cycle methods return <code>ints</code>, 
  +                which signal that 
  +                the container should take a certain action after the method. 
  +                For instance, the constant <code>EVAL_BODY_INCLUDE</code> 
  +                returned from <code>doStartTag()</code> tells the container to 
  +                include the tag's body in the JSP's output response. So a tag 
  +                which conditionally includes its body based on the value of one 
  +                of its attributes might be verified like this:
  +            </p>
  +<source><![CDATA[
  +tag.setValueThatResultsInInclude("correct value");
  +assertEquals(Tag.EVAL_BODY_INCLUDE, tag.doStartTag());
  +]]></source>
  +        </s2>
  +        <s2 title="Checking effects on page data">
  +            <p>
  +            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 
  +            <code>JspTestCase's</code> implicit objects to verify that the 
  +            tag has correctly modified the information. The following 
  +            snippet verifies that the <code>CatalogListTag</code> has placed a 
  +            collection of objects in the request under the key "catalogs":
  +            </p>
  +<source><![CDATA[
  +catalogListTag.doStartTag();
  +Collection catalogs = (Collection)request.getAttribute("catalogs");
  +assertNotNull(catalogs);
  +]]></source>
  +        </s2>
  +        
  +        <s2 title="Verifying tag output">
  +            <p>
  +                Use the <code>endXXX</code> method to verify that your tag's 
  +                methods have resulted in the correct data being written 
  +                to the response.
  +            </p>
  +            <note>
  +                    This example uses the <code>endXXX()</code> 
  +                    signature from Cactus 1.2 or above.
  +            </note>
  +<source><![CDATA[
  +public void endSomeTagTest (WebResponse response)
  +{
  +    String output = response.getText();
  +    assertEquals("<b>expected output</b>", output);
  +}
  +]]></source>
  +        </s2>
  +
  +    </s1>
   
  +    <s1 title="Special Cases">
  +        <p>
  +            There are a few scenarios in custom tag testing that deserve extra
  +            attention.
  +        </p>
  +        <s2 title="Iteration Tags">
  +            <p>
  +                To test a tag that repeats its body processing a number of 
  +                times, simply create a <code>do-while</code> loop that mimics  
  +                the life cycle of an iteration tag:
  +            </p>
  +<source><![CDATA[
  +//[...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);
  +]]></source>
  +            <p>
  +                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.
  +            </p>
  +        </s2>
  +        <anchor id="using_body_content"/>
  +        <s2 title="Body Tags">
  +            <p>
  +                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 
  +                <code>EVAL_BODY_INCLUDE </code> for instance). Nothing special 
  +                is required to test this sort of tag, since the tag is 
  +                unconcerned about its contents.
  +            </p>
  +            <p>
  +                Testing BodyTags--tags which actually perform some processing
  +                on their content--is a little trickier.
  +                BodyTags can choose to return a constant (
  +                <code>EVAL_BODY_TAG</code> in JSP 1.1, 
  +                <code>EVAL_BODY_BUFFERED</code> in 1.2)  from 
  +                <code>doStartTag()</code> which signals to the container that 
  +                the tag would like a chance to handle its own body. 
  +                If it receives this result, the container calls 
  +                <code>pageContext.pushBody()</code> to obtain a 
  +                <code>BodyContent</code>object. The <code>BodyContent</code> 
  +                object is passed to the tag through the tag's 
  +                <code>setBodyContent()</code>method. The container then uses 
  +                this object (the old out object is saved) to capture all of the 
  +                response writing that goes on in the body of the tag. After the 
  +                tag's body has been evaluated, the tag itself has a chance to 
  +                do something with the result of the evaluation in it's 
  +                <code>doAfterBody()</code> method.
  +            </p>
  +            <p>
  +                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:
  +            </p>
  +<source><![CDATA[
  +//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();
  +tag.setBodyContent(bodyContent);
  +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();
  +]]></source>
  +            <p>
  +                This sample does not fully replicate the container's handling of
  +                the tag (for instance, the tag would only receive the 
  +                <code>bodyContent</code> object if the result of 
  +                <code>doStartTag</code> indicated that it should do so). 
  +                However, in a test environment, you can make assumptions if 
  +                doing so simplifies the workings of the test. (You happen to 
  +                know that in this case <code>doStartTag</code> will always return 
  +                <code>EVAL_BODY_BUFFERED</code>). Similarly, this example omits what
  +                for the container would be an obligatory call to 
  +                <code>pageContext.popBody()</code>. The container calls this 
  +                method so that other tags do not reuse the <code>bodyContent</code>
  +                object used for this tag. If your test uses only a single tag, 
  +                you can skip this step.
  +            </p>
  +            <note>
  +                Again, you can check that the body of the tag was handled
  +                correctly by verifying the total output in the 
  +                <code>endXXX()</code> method.
  +            </note>
  +        </s2>
  +        <s2 title="TagExtraInfo classes">
  +            <p>
  +                Cactus does not offer any specific services to support the 
  +                testing of <code>TagExtraInfo</code> classes because they do 
  +                not depend on any of the implicit objects.
  +            </p>
  +        </s2>
       </s1>
   
  +    <anchor id="further_integration"/>
  +    <s1 title="Further Integration Testing">
  +        <p>
  +            You 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.
  +        </p>
  +        <s2 title="The test JSP">
  +            <p>
  +                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 <code>junit.framework.Assert</code>
  +                to do some simple checks on the effects of the tag. 
  +                Here is a short example of a JSP that exercises a tag:
  +            </p>
  +<source><![CDATA[
  +<%@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);
  +%>
  +]]></source>
  +           <p>
  +                It's a bad idea to put too many assertions into the 
  +                JSP. In the example above, the creation of a scripting variable 
  +                can <strong>only</strong> 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 
  +                <code>pageContext.include()</code> (See below for an example.)
  +           </p>
  +        </s2>
  +
  +        <s2 title="The TestCase">
  +            <p>
  +                To use the test JSP, include it from within a 
  +                <code>JspTestCase.</code> 
  +                The convenience function <code>pageContext.include()</code> 
  +                takes care of this nicely:
  +            </p>
  +<source><![CDATA[
  +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"));
  +}
  +]]></source>
  +            <p>
  +                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.
  +            </p>
  +            <note>
  +                Any output that the test JSP generates can be checked normally 
  +                in the <code>endXXX</code> method.
  +            </note>
  +            <p>
  +                Of course, using this strategy means that you need to put 
  +                the <code>test_some_tag.jsp</code> in the specified location 
  +                within your web application. If you are using JSP test case 
  +                your build script should already deploy the redirector JSP, so 
  +                it should be easy to include another JSP in the build process.
  +            </p>
  +        </s2>
  +    </s1>
     </body>
   </document>
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to