5.8 Testing a Form Tag and Refactoring Your Tests

5.8.1 Problem

You want to test for the existence of an HTML form.

5.8.2 Solution

Use the com.meterware.httpunit.WebForm class to test the form method and action.

5.8.3 Discussion

Adding HTML forms to a web application implies that the application is beginning to take on dynamic behavior. As your application gets more complex, you should continually refactor your tests in order to keep them as simple as possible. The solution outlined here shows how to test for an HTML form, as well as showing a refactored test fixture. Example 5-5 opens with a test for a basic HTML form.

Example 5-5. Refactored unit test
package com.oreilly.javaxp.httpunit;

import com.meterware.httpunit.*;
import junit.framework.TestCase;

public class TestNewsletter extends TestCase {
    private WebConversation webConversation;

    public TestNewsletter(String name) {
        super(name);
    }

    public void setUp(  ) throws Exception {
        this.webConversation = new WebConversation(  );
    }

    ...tests from earlier recipes are not shown here

    public void testSubscriptionForm(  ) throws Exception {
        WebForm form = getBlankSubscriptionForm(  );

        assertEquals("subscription form action",
                "subscription", form.getAction(  ));
        assertEquals("subscription form method",
                "post", form.getMethod().toLowerCase(  ));
    }

    private WebForm getBlankSubscriptionForm(  ) throws Exception {
        WebResponse response = getBlankSubscriptionPage(  );
        return response.getFormWithID("subscriptionForm");
    }

    private WebResponse getBlankSubscriptionPage(  ) throws Exception {
        return this.webConversation.getResponse(
                "http://localhost:8080/news/subscription");
    }
}

The HTML form we are testing will eventually allow the user to enter their name and email address to subscribe or unsubscribe from a newsletter. For now, it is sufficient to test that the form exists. Once the form is tested, you can move on to testing the content within the form as shown in the next recipe.

The test fixture shown in Example 5-5 is designed to make it easy to get to the WebForm object using the getBlankSubscriptionForm( ) method. As you write more and more tests, you should look for repeated functionality and refactor it into helper methods as shown here. Since most of the tests in this chapter require an instance of the WebConversation class, its initialization has been moved to the setUp( ) method.

Example 5-6 shows a refactored version of the servlet that was originally presented in Recipe 5.5. As you can see, the println( ) statements have been removed. Instead, the servlet uses RequestDispatcher to delegate page rendering to a JSP.

Example 5-6. Servlet that dispatches to a JSP
package com.oreilly.javaxp.httpunit;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

public class NewsletterServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req,
                         HttpServletResponse res)
            throws ServletException, IOException {

        RequestDispatcher dispatcher =
                req.getRequestDispatcher("subscription.jsp");

        dispatcher.forward(req, res);
    }
}

Using servlets in combination with JSPs is a much more realistic way to implement a complex web application. From the perspective of HttpUnit, the server-side architecture rarely matters. HttpUnit is simulating a web browser, so it does not need to know that the servlet is dispatching to a JSP.

Unit tests from earlier recipes tested NewsletterServlet when it was written using println( ) statements. After refactoring the servlet to use RequestDispatcher, the tests still pass. These tests provide reassurance that the servlet implementation change did not break things that used to work.

The final piece of the refactored web application is the JSP, shown in Example 5-7. Since the test only checks to see if the form exists, the JSP is simple, only generating the form.

Example 5-7. subscription.jsp
<html>
  <head>
    <title>Newsletter Subscription</title>
  </head>

  <body>
    <h1>Newsletter Subscription</h1>
    <form method="post" action="subscription" id="subscriptionForm">
    </form>
  </body>
</html>

Chapter Development

We wrote this chapter in roughly the same order as it is presented. The code, a web application for subscribing and unsubscribing from a newsletter, evolved as the recipes were written.

Writing a solid Ant buildfile that could easily compile and deploy the application to Tomcat was a major hurdle that took nearly as much time as writing most of the code. But this time was well worth the effort, because it made the test-first development of new features go incredibly quickly.

The initial servlet, as shown in Recipe 5.5, consisted of println( ) statements. Once we got to the point where we wanted to test HTML forms, however, we decided to refactor the servlet so it delegated page rendering tasks to a JSP.

While refactoring, we initially mistyped "subscription.jsp" as "subscription" in my RequestDispatcher logic. The existing unit tests failed. Once we fixed this, my HTML form test from Recipe 5.8 caught the fact that the JSP form action was set to "subscribe" instead of "subscription". Without unit tests, we would have had to manually click on every hyperlink in the web application in order to catch these errors. As your own apps grow, a full suite of unit tests becomes increasingly valuable.