JaVa
   

Functional Tests

The most intuitive way to test Web apps is to open your Web browser, type in the URL you want to visit, and then click off and verify that the app behaves as expected. This has nothing to do with automation, but still. . . . The next step is to use a specialized capture and replay tool that remembers your mouse movements, clicks, and keyboard hits during the first run to then automatically replay them. There is a wide choice of such tools on the market. Similar options are supported (but directly in Java) by HttpUnit. In contrast to what the name suggests, HttpUnit has little to do with unit testing. HttpUnit is an open source Java API for accessing Web sites without a browser and is ideally suited for automated unit testing of Web sites when combined with a Java unit testing framework such as JUnit. HttpUnit, including extensive documentation, is free and can be downloaded from [URL:HttpUnit]. We can use this functionality for "remote controlling" arbitrary Web apps, but also to test these apps. The following simple example shows how HttpUnit can be used for testing. Typically, functional tests are retrospective (as opposed to test-first); so we begin with the following simple servlet that is to be tested later:

import java.io.*;
import javax.servlet.http.*;
public class MyServlet extends HttpServlet {
 public void doGet(HttpServletRequest request,
 HttpServletResponse response) {
 doPost(request, response);
 }
 public void doPost(HttpServletRequest request,
 HttpServletResponse response) {
 try {
 String name = (String) request.getParameter("name");
 PrintWriter writer = response.getWriter();
 writer.println("<html><head><title>My Servlet"+
 "</title></head>" +
 "<body>Hello, " + name +
 "!</body></html>");
 writer.close();
 } catch (Throwable ex) {
 ex.printStackTrace();
 }
 }
}


All this servlet does is insert the passed parameter name into a reply page, Hello, <name>!. We test for simple access to the servlet, using an HTTP GET request to a URL (e.g., http://servername/servlet/MyServlet?name=Johannes):

import com.meterware.httpunit.*;
public class MyServletTest extends TestCase {
 private final String SERVLET_URI =
 "http://myserver/servlet/MyServlet";
 public void testGetRequest() throws Exception {
 WebConversation con = new WebConversation();
 WebRequest request = new GetMethodWebRequest(SERVLET_URI);
 request.setParameter("name", "Johannes");
 WebResponse response = con.getResponse(request);
 assertEquals("text/html", response.getContentType());
 }
}


The starting point is an instance of WebConversation, comparable to a browser session. This object returns an instance of class WebResponse when getResponse(WebRequest) is invoked. In this example, we create an HTTP GET request (GetMethodWebRequest) with a parameter (setParameter(...)). The response object can eventually be consulted for test verification. The first step tested only the content type of the returned page. Let's add more verification items:

public void testGetRequest() throws Exception {
 WebConversation con = new WebConversation();
 WebRequest request = new GetMethodWebRequest(SERVLET_URI);
 request.setParameter("name", "Johannes");
 WebResponse response = con.getResponse(request);
 assertEquals("text/html", response.getContentType());
 assertEquals("My Servlet", response.getTitle());
 assertTrue(response.getText().indexOf("Hello, Johannes!") != -1);
}


We can see that the class WebResponse allows us to access both single html elements—getTitle()—and the full HTML text—getText()—of the response page. While verifying the HTML code soon becomes unmanageable, the targeted access to tables, forms, and links allows us to verify exactly those elements of an HTML page that are important for correct functioning of the app. We can use HttpUnit to fill in information in Web forms, follow links, and use frames, cookies, and SSL. But it does not support browser script languages like JavaScript. [2] To understand this better, we will extend the servlet by a form that can be used to change the parameter name and resend the request:

public class MyServlet extends HttpServlet {
 ...
 private final String SERVLET_URI = "MyServlet";
 public void doPost(HttpServletRequest request,
 HttpServletResponse response) {
 try {
 String name = (String) request.getParameter("name");
 PrintWriter writer = response.getWriter();
 writer.println("<html><head><title>My Servlet" +
 "</title></head>" +
 "<body>Hello, " + name + "!");
 writer.println("<form name=\"form1\" action=\"" +
 SERVLET_URI + "\">");
 writer.println("<input type=text name=\"name\"" +
 value=\"" + name + "\">");
 writer.println("<input type=submit name=\"button\" +
 value=\"Change Name\">");
 writer.println("</form></body></html>");
 writer.close();
 } catch (Throwable theException) {
 theException.printStackTrace();
 }
 }
}


The following test specifically accesses the form elements and its children:

public void testGetRequest() throws Exception {
 WebConversation con = new WebConversation();
 WebRequest request = new GetMethodWebRequest(SERVLET_URI);
 request.setParameter("name", "Johannes");
 WebResponse response = con.getResponse(request);
 ...
 WebForm form = response.getFormWithName("form1");
 assertEquals("Johannes", form.getParameterValue("name"));
 assertEquals("Change Name",
 form.getSubmitButton("button").getValue());
}


Screenshot shows what the page now looks like in the browser.

Java Click To expand
Screenshot: MyServlet in the browser.

Another test checks whether or not entering a new name and clicking the Change Name button works as desired:

public void testChangeName() throws Exception {
 WebConversation con = new WebConversation();
 WebRequest request = new GetMethodWebRequest(SERVLET_URI);
 request.setParameter("name", "Johannes");
 WebResponse response = con.getResponse(request);
 WebForm form = response.getFormWithName("form1");
 request = form.getRequest("button");
 request.setParameter("name", "Frank");
 response = con.getResponse(request);
 assertTrue(response.getText().indexOf("Hello, Frank!") != -1);
}


In theory, HttpUnit allows us to also verify the page layout, because we can use the method response.getDOM() to get a DOM-compliant tree representation of a page. [3] In practice, however, such detailed tests often prove to be too fragile to deserve automation. To do the testing in the form shown here, we have to install the complete Web app on the server. This and the fact that we are testing only on the highest functional level shows clearly that these test cases are not unit tests, but functional tests on system level. As we proceed we will see how HttpUnit can also be used for "real" unit tests. There exist, meanwhile, a couple of other HttpUnit-like frameworks, for example, the Web app Testing Framework [URL: WATF]. The drawback of using HttpUnit or a like tool as a functional testing tool is that it requires coding knowledge. As a potential alternative, we could use either a capture and replay tool or develop a project-specific framework allowing us to specify functional tests in textual form, for example, XML as in Canoo-Webtest [URL:Webtest]. This means that we could kill two birds with one stone:

[2]But there is a special test framework for it, JsUnit [URL:JsUnit]. [3]DOM stands for Document Object Model (see Glossary).


JaVa
   
Comments