« I second the motion! | Main | Caution! Test in Progress »

July 11, 2004

TagTestHelper - you mock me!

I have to admit I'd already decided exactly how this post was going to end well before starting the coding to support my hypothesis. Having now finished the coding, I've completely changed my mind!

I was going to make the case for not making testing infrastructure overly complex unless it really needs it. The practical context I used is testing custom JSP tags and the difficulties in correctly creating the PageContext object necessary for a complete test. My general approach to these things was:

  1. extract a method out of doStartTag to handle the processing and output required by the tag (for the sake of the argument, let's call this method generate()
  2. pass as parameters to generate() all the values it needed to complete it's operation, incuding a Writer of some sort to hold the result
  3. invoke generate() directory from the unit test, supplying a StringWriter object to hold the result
  4. Trust that generate() will be called appropriately from doStartTag() or there will be an acceptance test which will break if this is not the case.
Here's my test tag already constructed to support this method of testing.
01 public class TimestampTag extends TagSupport {
02     private static final SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm");
03 
04     public int doStartTag() throws JspException {
05         try {
06             generate(Calendar.getInstance(), pageContext.getOut());
07         catch (IOException e) {
08             throw new JspException(e);
09         }
10         return SKIP_BODY;
11     }
12 
13     public void generate(final Calendar timestamp, final Writer writerthrows IOException {
14         writer.write(TIMESTAMP_FORMAT.format(timestamp.getTime()));
15     }
16 }

I haven't had to write a tag so far that's needed testing any deeper than this and I was convinced that alternative techniques (using mocks, in particular) was going to add way too much overhead to the test to make the added advantage of being able to call doStartTag directly worth the excess baggage.

Background duly dispensed with, I've included sample tests using both my naive approach and using mock objects below.

01 public class TimestampTagTest extends TestCase {
02     private TimestampTag tag = new TimestampTag();
03     private TagTestHelper helper = new TagTestHelper(tag);
04     private static final Calendar NOW = Calendar.getInstance();
05     private static final String EXPECTED_RESULT = new SimpleDateFormat("dd/MM/yyyy HH:mm").format(NOW.getTime());
06 
07     public void testTagContentsNaive() throws IOException {
08         final StringWriter writer = new StringWriter();
09         tag.generate(NOW, writer);
10         assertEquals(EXPECTED_RESULT, writer.toString());
11     }
12 
13     public void testDoStartTagReturnsCorrectValue() throws JspException {
14         helper.assertDoStartTag(TagSupport.SKIP_BODY);
15     }
16 
17     public void testTagContentsMock() throws IOException, JspException {
18         helper.getOutWriter().setExpectedData(EXPECTED_RESULT);
19         tag.setPageContext(helper.getPageContext());
20         tag.doStartTag();
21         helper.getOutWriter().verify();
22     }
23 }

As you can see, there is very little difference between the size of the two mechanisms for testing the tag (testContentsNaive versus testContentsMock). I must admit the TagTestHelper class makes all this sort of stuff much, much easier, although I still contend the naive method results in a slightly easier-to-read test. Given this, I'm about to abandon my naive tag testing idiom and use the TagTestHelper from now on.

Note: using an alternative tag testing approach like that provided by the in-container Cactus framework can also produce tests of a similar quality to those allowed via mock objects however the effort required for configuration is substantially greater. That said, there is probably a place for some form of in-container testing in all J2EE projects.

Posted by Andy Marks at July 11, 2004 06:08 PM

Comments

Where is the TagTestHelper class from?

Posted by: Marty Andrews at July 12, 2004 08:15 AM

Marty,

TagTestHelper is one of the very useful helper classes available from the Mock Objects J2EE jar.

Posted by: Andy Marks at July 12, 2004 10:58 AM

Post a comment

Thanks for signing in, . Now you can comment. (sign out)

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)


Remember me?