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]