This page demonstrates the usage of the tag test framework. The TagTestModule and the corresponding adapters, BasicTagTestCaseAdapter and TagTestCaseAdapter, can simulate simple tags or complex interactions of nested body tags without the need of a running server and without embedding the tags in test JSP pages.
The following example shows the implementation of a body tag that manipulates its body content. It takes a Collection from the session and iterates through the elements. For each element it creates an HTML table row with a label and the current body content. It stores the current element in the request. A nested Struts <bean:write> or JSTL <c:out> tag can be used to write the element as a string to the page.
If you store a List with one entry Entry1 in the session and specify the following JSP code
<mytags:enumtag label="myLabel"> <bean:write scope="request" name="currentObject"/> </mytags:enumtag>
then you'll get the following HTML output:
<table> <tr> <td> myLabel </td> <td> Entry1 </td> </tr> </table>
Here's the code of the tag.
public class TableEnumTag extends BodyTagSupport { private String label; private Iterator iterator; public void setLabel(String label) { this.label = label; } public void release() { super.release(); label = null; iterator = null; } private void copyNextObjectToRequest() { Object nextAttribute = iterator.next(); pageContext.setAttribute("currentObject", nextAttribute, PageContext.REQUEST_SCOPE); } public int doStartTag() throws JspException { try { Collection col = (Collection)pageContext.getAttribute("currentCollection", PageContext.SESSION_SCOPE); iterator = col.iterator(); if(iterator.hasNext()) { pageContext.getOut().println("<table>"); copyNextObjectToRequest(); return EVAL_BODY_BUFFERED; } return SKIP_BODY; } catch(IOException exc) { throw new JspException(exc.getMessage()); } } public int doAfterBody() throws JspException { String bodyString = getBodyContent().getString(); JspWriter out = getBodyContent().getEnclosingWriter(); try { if(null != bodyString && bodyString.length() != 0) { out.println("<tr>"); out.println("\t<td>"); out.println("\t\t" + label); out.println("\t</td>"); out.println("\t<td>"); out.println("\t\t" + bodyString); out.println("\t</td>"); out.println("</tr>"); } if(iterator.hasNext()) { copyNextObjectToRequest(); getBodyContent().clear(); return EVAL_BODY_AGAIN; } out.println("</table>"); return SKIP_BODY; } catch(IOException exc) { throw new JspException(exc.getMessage()); } } public int doEndTag() throws JspException { return EVAL_PAGE; } }
Now we want to write a test for this complex tag. We'll use a Collection with three entries and a nested <bean:write> tag. Of course we could test the tag with other body contents as well. Check out the release for more test examples of TableEnumTag.
public class TableEnumTagTest extends BasicTagTestCaseAdapter { protected void setUp() throws Exception { super.setUp(); ArrayList list = new ArrayList(); list.add("Entry1"); list.add("Entry2"); list.add("Entry3"); MockHttpSession session = getWebMockObjectFactory().getMockSession(); session.setAttribute("currentCollection", list); } public void testBodyContent() throws Exception { Map attributeMap = new HashMap(); attributeMap.put("label", "myLabel"); NestedTag nestedTag = createNestedTag(TableEnumTag.class, attributeMap); attributeMap = new HashMap(); attributeMap.put("scope", "request"); attributeMap.put("name", "currentObject"); nestedTag.addTagChild(WriteTag.class, attributeMap); processTagLifecycle(); BufferedReader reader = getOutputAsBufferedReader(); assertEquals("<table>", reader.readLine().trim()); for(int ii = 1; ii <= 3; ii++) { assertEquals("<tr>", reader.readLine().trim()); assertEquals("<td>", reader.readLine().trim()); assertEquals("myLabel", reader.readLine().trim()); assertEquals("</td>", reader.readLine().trim()); assertEquals("<td>", reader.readLine().trim()); assertEquals("Entry" + ii, reader.readLine().trim()); assertEquals("</td>", reader.readLine().trim()); assertEquals("</tr>", reader.readLine().trim()); } assertEquals("</table>", reader.readLine().trim()); } }
As you can see we create the tag and add a Map with the attributes. A NestedTag is a wrapper for a normal tag class that is able to maintain child tags and static body content. You only have to deal with NestedTag if you are using child tags. The framework simulates the container behaviour by populating the attributes and performing the tag lifecycle.
Tags usually create HTML output. As in the servlet test module, the framework provides this output in different formats, e.g. as String, as Reader or as parsed XML. In this test we use the Reader output.
Not all tag tests are as complex as in the previous example. It's also possible to create the tag, call doStartTag() and test the output. It depends on the implementation of the tag and your test requirements. Please check out the release for more examples.