HttpUnit: A civilized way to test Web applications in WebSphere Studio

Just as JUnit is an excellent open-source tool for Java testing, HttpUnit is an equally good test tool for Web development. This article shows you how to use it with WebSphere Studio. It provides an API that mimics the behavior of a Web browser and makes direct calls to the code you are testing. The HttpUnit API can emulate a number of browser behaviors, including form submission, JavaScript, HTTP authentication, and use of cookies. HttpUnit also enables you to analyze the content that is returned when a Web page is loaded.

Kulvir Singh Bhogal (kbhogal@us.ibm.com), IBM Software Services for WebSphere Consultant, Fort Worth, TX

Photo of Kulvir Singh BhogalKulvir Singh Bhogal is an IBM Software Services for WebSphere Consultant, devising and implementing Java-centric solutions at customer sites across the United States. You can reach Kulvir at kbhogal@us.ibm.com .



10 March 2003

Introduction

Testing. For many of us developers, the word makes us cringe. This stigma about testing can probably be traced to the tedium of testing in the past. Fortunately, frameworks are now available to ease the testing burden. Today's high-pressure projects with project managers breathing down your neck with printouts of schedules from Microsoft Project is an environment that can breed errors in your code. Accordingly, thorough testing is more important than ever.

But what about the Web?

One testing framework that has gained a lot of acclaim recently is JUnit. Using JUnit, you can incrementally build test suites during your development process. Such automated unit testing is an integral part of Extreme Programming (XP) methodology. JUnit is fine for Java™ programmers, but what about those of us who do Web development? The open-source Java APIs of HttpUnit can come to our rescue. In this tutorial, we will build a small Web site with IBM® WebSphere® Studio Application Developer Version 5 (hereafter called Application Developer), then use HttpUnit to test our sample site. (A note on versions: while the steps and screen shots below apply to Version 5, you should encounter only slight differences in a few places if you are using Version 4.)

Traditional (Neanderthal) ways to test Web sites

I'll admit it -- in the past, I have created small "driver" pages with forms to feed data to Java servlets that I am trying to test. I have also manually tacked on name-value pairs to the end of URLs, such as http://www.somesite.com/myPage?myVarName=myTestValue. Both of these methods can work. However, they can be like trying to mow your lawn with a pair of scissors. There has to be a better way.


HttpUnit

Unlike with the test methodologies above, HttpUnit does not require you to use your browser. With HttpUnit, you can make direct calls to the code you are testing. HttpUnit in essence mimics a Web browser, and the HttpUnit API can emulate a number of browser behaviors, including:

  • Form submission
  • JavaScript
  • HTTP authentication
  • Cookies

You can also use the HttpUnit API to analyze the content returned when a Web page is loaded.

Why HttpUnit instead of the alternatives?

With the growth of the Web and the conspicuous nature of Web programming errors, many commercial test products hit the market with elaborate GUIs to guide the developer through the testing process. The open-source HttpUnit, on the other hand, is free of licensing fees and very simple to use (as you'll see below). It is this simplicity that makes it so popular. While it lacks a pretty GUI, the availability of the HttpUnit Java source code and its simple API let developers create their own testing solutions to fit for the needs of their particular organizations. Using HttpUnit as a foundation, you can build complex test suites at minimal cost.

Dissecting HttpUnit

HttpUnit can be broken down into two core components:

  • A Web client that sends requests and receives responses
  • A collection of methods that analyze and verify response content

Does using HttpUnit equate to unit testing?

Contrary to what its name might imply, HttpUnit does not actually address unit testing. In fact, HttpUnit is more geared towards functional or "black box" testing. Tests that you write using HttpUnit will query the Web server externally and enable you to analyze the responses received. Functional testing plays an important role in XP methodology, which stresses functional testing so that developers can get feedback about the overall state of their system. With unit testing, sometimes the big picture gets lost. Automated functional testing can save developers from the drudgery of manually inspecting areas of a site as the entire site makes its way to production. In a Web environment, some experts feel that each request-response cycle is atomic, and that these atomic cycles can in turn be treated as a single unit of code, and therefore use of HttpUnit can be considered unit testing. Rather than add to this philosophical discussion, let's move on and get to work!


Sample application

Our sample application consists of an HTML file where one enters a temperature to be converted from Celsius to Fahrenheit or vice versa. The conversion process will be carried out by a servlet and results will be returned to a JSP for presentation. Of course, we could do all of this on the client side using JavaScript, but using the different Web components will demonstrate the value of testing with HttpUnit.


Creating a test server

This section assumes that you don't have a test server set up on your machine. To create a server in Application Developer, open the Server perspective. In the Server configuration pane, right-click on the Servers folder and select New => Server and server configuration.

newserver.gif

On the next screen, specify a Server name of TestServer and choose a Server type of WebSphere Version 5.0 Test Environment.

serverconfig.gif

Click Next. If asked if you would like to create a new server project with the name Servers, click Yes.

You are presented with a dialog in which you can specify the HTTP port which the test server will listen to. Keep the default setting of 9080 and click Next.

portsettings.gif

Importing the project EAR

You can access the Temperature Converter project EAR file by downloading tempprojectfiles.zip. In the Studio workspace, select File => Import.

Choose an import source of EAR file. On the next screen, browse to your EAR file on your hard disk, name your project TemperatureConversionProject, and click Finish.

specifyear.gif

Testing the application manually

At this point, you should be able to test the application manually. Start the test server and point your Web browser to http://localhost:9080/TempConvertHttp/TemperatureEntry.html. This should bring up the Temperature Conversion Application:

appscreen.gif

Sample results

Experiment with the application to see how it works. The following screen shot shows the results you should see if you convert 98 Celsius to Fahrenheit:

sampleresults.gif

A quick science lesson

Here are the formulae for converting from Fahrenheit to Celsius and vice-versa:

C = temperature in degrees Celsius F = temperature in degrees Fahrenheit

To convert a Fahrenheit temperature into Celsius: C = (F-32)*(5/9)
To convert a Celsius temperature into Fahrenheit: F = ((9/5)*C)+32

Dissecting the TempConverterServlet

The code below is for TempConvertServlet, which is the engine that does the temperature conversions. As you can see from the code, it obtains the temperature to be converted and the task from the request, calculates the result, and then returns this result along with the input temperature and task. It then passes control to conversionResult.jsp.

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet converts temperatures from Celsius to Fahrenheit or vice versa
 */
public class TempConverterServlet extends HttpServlet {

	/**
	* @see javax.servlet.http.HttpServlet#void (javax.servlet.http.HttpServletRequest,
	*     javax.servlet.http.HttpServletResponse)
	*/
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
		String convertMe = req.getParameter("convertMe");
		double whatToConvert = Double.parseDouble(convertMe);
		System.out.println("Converting" + whatToConvert);
		double converted =0.0;
		String conversionType = req.getParameter("howToConvert");
		if (conversionType.equals("C2F"))
		{
			converted = (9*(whatToConvert)/5)+32;
		}
		else if (conversionType.equals("F2C"))
		{
			converted = (5*(whatToConvert-32))/9;
		}
		String convertedString = Double.toString(converted);
		req.setAttribute("ConversionResult",convertedString);
		req.setAttribute("ConversionSource",convertMe);
		req.setAttribute("ConversionMethod",conversionType);
		RequestDispatcher dispatcher =
		    getServletContext().getRequestDispatcher("conversionResult.jsp");
		dispatcher.forward(req,resp);

	}

	/**
	* @see javax.servlet.http.HttpServlet#void (javax.servlet.http.HttpServletRequest,
	*     javax.servlet.http.HttpServletResponse)
	*/
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
		doGet(req, resp);
	}

}

Reporting results with conversionResult.jsp

Take a look at the code for conversionResult.jsp. It simply extracts the information from the request object that we had stored earlier in TempConverterServlet. You will see some embedded scriptlets to facilitate a dynamic presentation of the results.

Analyzing results

Notice that we present our results in the JSP in the form of an HTML table. Below you will see a matrix layout of the table and a visualization of how the table can be broken down into rows and columns. In the matrix below, a table cell is described in the format of [i][j], where i is the row and j is the column. Looking at the matrix below, 208.4 is located in row 1, column 2 (rows and columns are zero-indexed). We will use this information in our automated testing.

tablestructure.gif

Creating your test Java project

In Application Developer, create a new Java project named TestHttpProject. In the Projects tab of the Java settings, check the TempConvertHttp project.

pickproject.gif

Adding external JARs

In the Libraries tab, click Add External JARs and point to the following JAR files: httpunit.jar, js.jar, junit.jar, servlet.jar, Tidy.jar. All of these JAR files are located in the HttpUnit project ZIP file, which you can download from sourceforge.net.

javaprojectsettings.gif

Once you have added the JARs, click Finish.

Importing your test class

Right-click on TestHttpProject, select Import => File System, and then click Next. Browse to the directory that contains the extracted project files located in the project ZIP file and check TempConverterTester.java, then click Finish.

importjavafile.gif

Analyzing the TempConverterTester

TempConverterTester extends the class junit.framework.TestCase. This derivation is necessary to set up our test as a JUnit test. Reflection is used by the JUnit framework to look for methods in our class which begin with test. This is done when the TestSuite constructor is invoked in the code:

public static Test suite()
{
	return new TestSuite(TempConverterTester.class);
}

The suite static method is called in our main method with the code:

public static void main(String[] args)
{
	junit.textui.TestRunner.run(suite());
}

Scrutinizing the testConvertsCorrectlyWithServletUnit() Method

public void testConvertsCorrectlyWithServletUnit()
    {

        try
        {
            System.out.println("Starting Conversion Results Test with ServletUnit");
            ServletRunner sr = new ServletRunner();
        sr.registerServlet( "TempConverterServlet", TempConverterServlet.class.getName() );
            ServletUnitClient sc = sr.newClient();
        WebRequest request = new PostMethodWebRequest(
"http://localhost:9080/TempConvertHttp/TempConverterServlet" );
          request.setParameter("convertMe", "100" );
      request.setParameter("howToConvert","C2F");
            WebConversation wc = new WebConversation();
      WebResponse resp = wc.getResponse( request );
          String[][] tableContents = resp.getTables()[0].asText();
            assertEquals("100",tableContents[0][2]);
            assertEquals( "212.0",  tableContents[1][2] );
            System.out.println("Conversion Results Test with ServletUnit Completed");
            System.out.println("----------------------------------------");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

    }

Let's take some time to analyze this method, as it is the heart of our test:

ServletRunner sr = new ServletRunner();
sr.registerServlet( "TempConverterServlet",
TempConverterServlet.class.getName() );

We first instantiate an instance of ServletRunner, which is part of the ServletUnit framework, a companion framework that ships with HttpUnit. The ServletUnit framework lets one test in a "simulated servlet container". We then register our servlet in ServletRunner.

Specifying request parameters

In the HttpUnit API, the WebRequest object represents a request to our server:

WebRequest request = new PostMethodWebRequest(
"http://localhost:9080/TempConvertHttp/TempConverterServlet" );

We use the setParameter method of the WebRequest object to set parameters and their corresponding values:

request.setParameter("convertMe", "100" ); 
request.setParameter("howToConvert","C2F");

This is a much more elegant method than adding name-value pairs to the end of a URL, as described above. For larger testing endeavors, the organization of the setParameter methodology is much better.

Talking to the server

Our actual conversation with the server takes place with the code:

WebConversation wc = new WebConversation(); 
WebResponse resp = wc.getResponse( request );

The WebConversation is an implementation of the HttpUnit WebClient class. In essence, WebConversation acts like a Web Browser. Where the WebRequest object represents a request to the server, the WebResponse object encapsulates the reply. You can do much more with the WebConversation than is presented in this simplistic scenario. A WebConversation object can maintain advanced client state attributes like cookies, relative URLs, and framesets.

Analyzing the response

The HttpUnit API lets you inspect the response from a request. As you recall, the servlet passes control back to conversionResult.jsp. If things are working with our temperature conversion utility, a conversion from 100 Celsius should yield 212 Farenheit.

analyzeresponse.gif

If you recall, in the resulting JSP, the source temperature value is found in table cell [0][2], and the result is in table cell [1][2].

String[][] tableContents = resp.getTables()[0].asText();

The code resp.getTables()[0] lets us access our first (and only) table in our resulting page. Using the HttpUnit API, we are able to extract the table contents from our resulting page in a two-dimensional String array.

Verifying the results

We verify that our application is converting temperatures directly with the code:

assertEquals("100",tableContents[0][2]); 
assertEquals( "212.0", tableContents[1][2] );

Here we tap into the offerings of JUnit. We use its assertEquals method to make sure that our cell contents are equal to what we expect them to be. When you run the TempConverterTester class, you should see this in your console:

run.gif

You might want to play around with the code of TempConverterServlet and deliberately alter the conversion method to see how the test will react. Below is a screen shot of the results of a test gone bad:

failure.gif

Conclusion

This article described HttpUnit, then introduced you to a sample application in which HttpUnit was applied. You saw how HttpUnit, JUnit, and ServletUnit can be used automate Web application testing. According to the XP philosophy, both black-box testing and unit testing are essential for creating stable Web applications. The open-source APIs of HttpUnit, JUnit, and ServletUnit can be used to create full-fledged, automated testing solutions that you can customize for the Web site testing needs of your enterprise.


Download

DescriptionNameSize
Code sampletempprojectfiles.zip  ( HTTP | FTP ).1 MB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=14173
ArticleTitle=HttpUnit: A civilized way to test Web applications in WebSphere Studio
publish-date=03102003