Build a Java Web App Using HttpUnit and the Test-driven Methodology, Part I

Unit performs automated testing for standalone Java applications that either run on the command line or serve as components. If you develop large-scale or distributed applications, however, you are most likely going to work with Java Web applications. Because Web applications communicate via HTTP rather than Java method calls, JUnit is poorly equipped to test Web applications. HttpUnit comes to the rescue. This freely available tool provides Web testing functionality that complements JUnit.

You can work with HttpUnit in isolation, or you can mix HttpUnit with JUnit, which allows you to utilize JUnit’s reporting and regression features. Although it shares a similar name to JUnit, the HttpUnit library is more general-purpose than your average testing framework. It contains code for simulating a browser, including partial JavaScript support. HttpUnit is essentially a programmable Web client. You could even use it to write a Web spider that performs crawling and screen scraping.

The HttpUnit client API enables you to write tests that simulate a user accessing a Web-based application via a browser. It has many of the functions you expect a browser to have: cookie management for sessions, parsing of HTML content, form submittal via the GET and POST methods, authentication, and other features. You can check for certain content on the page, link by link and form by form, allowing you to verify that the application returns the proper results.

Recorded Macros vs. Programmatic API
The two primary methods for performing automated tests on Web applications are: [1] via recorded macros that can be played back and [2] via a programmatic API that parses HTTP responses. Recorded macros require frequent revisions and tend to be brittle. A programmatic API requires picking apart the HTML files received from the Web server and building object trees filled with HTML elements and textual content. This more flexible approach minimizes test maintenance in the most common cases and enables the testing of complicated functions in Web applications that return highly variable or complex HTML response pages. This is the approach HttpUnit takes.

The downside of API-based automated testing versus recorded macros is that the API-driven approach takes a little longer to set up. The quick setup time that pointing and clicking offers is hard to beat. Nevertheless, applications tend to evolve significantly over time, so the time spent maintaining tests tends to outweigh time spent setting tests up. Furthermore, the recorded macro approach suffers from the serious limitation that you must have the application code written before you can build the test for it. If you adhere to the test-first methodology, as extreme programming recommends, you are out of luck because you must have a working application to interact with before you can record your macro for playback.

This article is the first in a two-part series that goes through the paces of building a Web application with HttpUnit as a part of the development process. It focuses on a rudimentary phone list application that lets the user create, edit, and delete contacts and get a list of contacts. For simplicity, it avoids building database persistence to focus on the development process rather than the setup and installation process. However, it goes into coding that allows database persistence to be easily added later.

Requirements: You should have at least a rudimentary knowledge of what the J2SE 1.4 SDK is and how to use Ant. You should also know how to set up a stock distribution of Tomcat. This article covers Struts as well, but you do not have to know anything about it other than being able to download the Struts distribution and unpacking it. (For more information on Struts, consult the Struts documentation at jakarta.apache.org/struts. Struts is a useful Web application framework that improves maintainability of application code and is worth knowing if you plan to write large applications.)

Set the Development Environment
To get started with HttpUnit, download the distribution from www.httpunit.org. The latest version as of the writing of this article is 1.5.4, released in August 2003. The HttpUnit distribution includes both the source files in a subdirectory called src and the object files in a JAR file underneath the lib directory.

As a simple, instructive exercise for you first-time HttpUnit users, type “ant run-example” in the main directory. This will execute an example unit test written for HttpUnit. The example code is in examples/Example.java underneath the root. Table 1 explains what the example does.

First, set up the development environment. In the sample code included with this article, you’ll find files called phonelist.tgz and phonelist-example-files.tgz. Versions of each of the two files also are available for other compression tools with endings of “tbz2” and “zip”. Select whichever one is appropriate based on your platform and compression tool: the “.tgz” file is packed with tar and gzip; the “.tbz2” file is packed with tar and bzip2; the “.zip” file is packed with zip. This article gives instructions only for the “.tgz” file because it is the most prevalent format on Unix. The phonelist.tgz archive contains the fully functional application. The phone-example-files.tgz archive contains the files you will use to follow along with the sample development session.

Download or copy the phonelist.tgz file to your home directory. Unpack the file with the following commands:

mkdir ~/projectscd ~/projectstar xzf ~/phonelist.tgzcd phonelist

The “mkdir ~/projects” command results in an error if the projects directory already exists, which is fine. You should just make sure it exists. To try out the application, first make sure that Tomcat is running and that the values of tomcat.install.dir and struts.install.dir are correct for your environment. Then, type “ant deploy”. That should build the application and deploy it to Tomcat, where you can then access it via http://hostname:8080/phonelist. You should replace hostname with the actual host name of the computer running Tomcat. The application should be self-explanatory. You may want to look through the build.xml file in ~/projects/phonelist, because you will shortly go through the process of creating a software application.

Build and Implement an Electronic Phone List
Now proceed through the development cycle using HttpUnit. This article guides you step by step from nothing to having the completed code. You start out with the idea to build an electronic phone list. Your first implementation step (i.e., the part after business requirement solicitation and application design) should be to create the directory tree and write up a foundational build.xml file so that you can begin coding and testing.

First, delete the phonelist directory tree and the phonelist.tgz archive with the following commands:

rm -fr ~/projects/phonelistrm -f ~/phonelist.tgz

Now you are at the same point you would be at if you had just started a software project. Create the directory tree to house the files you will create with the following commands:

mkdir ~/projects/phonelistcd ~/projects/phonelistmkdir srcmkdir src/webmkdir src/WEB-INFmkdir src/WEB-INF/classesmkdir src/WEB-INF/classes/commkdir src/WEB-INF/classes/com/abcincmkdir src/WEB-INF/classes/com/abcinc/phonelistmkdir src/testmkdir src/test/commkdir src/test/com/abcincmkdir src/test/com/abcinc/phonelistmkdir src/test/com/abcinc/phonelist/test

The next step is to create a rudimentary build.xml file so that you can incrementally develop and test with simple, single-line commands that perform automated builds and tests. Type the following into a file called build.xml in the ~/projects/phonelist directory:

                                                                                

Make sure you change the values of the tomcat.install.dir and struts.install.dir properties so that they accurately reflect the locations of Tomcat and Struts. Save what you have now. You have just typed in an initialization target (target, you may recall, is Ant parlance for a series of related instructions packaged together as one unit). The initialization target defines properties you will use later, such as the location of Tomcat, the location of external libraries, and the locations of the source files.

Define Targets for Your Web App
Next, define some Ant targets that actually perform actions. For a Web application, at minimum you should be able to perform the following two tasks:

  1. Clean the directory, wiping out any object files or copied files so that you can start fresh with just the source code
  2. Compile and deploy the application to the Web server (Tomcat in this case)

The first task you will define in build.xml is a target named “clean” that will clean out the directory tree of any object files or processed files so you can do a fresh build. The second task you will define in build.xml is a target named “deploy” that will compile all of the classes, package the classes together with other Web files and descriptors into a WAR file, and then deploy the WAR file to the Web server. In addition, you will define a third task called “package-war” that will compile all of the classes and package together the unprocessed Web files (JSPs, images, property files, etc.) and Java classes into a WAR file that can be deployed to the Tomcat deployment directory (normally called “webapps” in a standard Tomcat distribution). The “package-war” target merely makes visible and public the intermediate steps of compiling and packaging files together into a WAR file. The “deploy” target ends up calling the “package-war” target.

The “clean” target is fairly easy to code up. Place the following XML snippet directly after the tag that closes the element but before the closing tag:

        

The “deploy” target is more complicated, so break it down into multiple steps. It needs to do the following:

  1. Compile the servlets and other Java classes
  2. Assemble compiled classes and unprocessed files (such as JSPs, images, style sheets, and XML deployment descriptors) into a WAR file
  3. Deploy the application by copying the WAR file to the Tomcat directory

Compiling the Java classes and then assembling the compiled Java classes and all the unprocessed files together into a WAR file can be grouped together logically into a “package-war” target. Users can invoke this target directly in Ant.

A good way to figure out which targets you should create is to work backwards from your goal. You want to deploy a WAR file to Tomcat, so that means you need the WAR file with all of the files in the application bundled up. The easiest way to create this WAR file is to zip up a directory tree with the layout of the files matching the layout of the intended WAR file. Remember that a WAR file is simply a ZIP file with specific rules on which files need to be present and where certain files should go in the directory tree. To create the directory tree with the unpacked layout and content of the files, follow these steps:

  1. Set up the directories so that you have a place to put files.
  2. Copy files that do not need processing over to the unpacked layout directory tree. This will include JSPs, XML descriptor files, images, and other MIME types.
  3. Compile classes such as servlets. The object code is saved directly in the unpacked layout directory tree.

To set up the directories, enter the following snippet into build.xml following the “clean” target you just entered:

                    

All of the JSPs, images, style sheets, and other files not needing compilation go directly in the unpacked-layout directory. The XML descriptors go in unpacked-layout/WEB-INF. The Java class files go in unpacked-layout/WEB-INF/classes. The JAR files for your libraries go in unpacked-layout/WEB-INF/lib.

The next step is to copy files not needing compilation into the unpacked-layout tree. Type the following in build.xml after the “create-unpacked-layout-dirs” target above:

                                                                                                                                                                            

The “copy-unprocessed-files” target above first copies Web files such as JSPs directly into unpacked-layout, then copies XML descriptors into unpacked-layout/WEB-INF. After that, it copies resource properties or other resource files into unpacked-layout/WEB-INF/classes, and then copies JAR files into unpacked-layout/WEB-INF/lib.

Now turn to the files requiring compilation. Add the following snippet of XML code to build.xml after the “copy-unprocessed-files” target above:

                        

The “compile-classes” target compiles all the files from WEB-INF/classes in the source tree. Recall that the location of the source tree was specified in the “init” target you created. The location was placed in the “src.dir” global property. The compiler uses the classpath specified in the “init” target. The classpath was placed in the “classpath” global property. The compiled object files are placed in the unpacked-layout/WEB-INF/classes directory.

You have now achieved the desired three steps:

  1. Set up the directories for the unpacked layout
  2. Copied files that do not need processing over to the unpacked layout directory tree
  3. Compiled classes such as servlets and placed the object code in the unpacked layout directory tree

Group Targets Together
Create a new target that groups together the three targets you just created. The new target will be called “build-unpacked”, and it is very short. Enter the following code snippet after the “compile-classes” target:

  

The target is empty. It just invokes the init target to initialize property settings and the three other targets you previously created. You now have the ability to build the entire application and lay out the files in the structure mandated by the WAR standard.

The obvious next step is to turn the unpacked layout into a WAR before deploying the WAR to Tomcat. The following build.xml snippet creates a directory in which to place the WAR file:

        

You should enter the “create-deploy-dir” target right after the “build-unpacked” target you just created. Now you have the unpacked layout of files and a place to store the WAR file.

The next step is to create the WAR file. WAR files must adhere to a defined layout. Put the following snippet of XML code in build.xml right after the “create-deploy-dir” target:

                                                                                                                            

The “package-war” target makes use of the “build-unpacked” target to create the unpacked layout and the “create-deploy-dir” target to create the directory for the WAR file. The “package-war” target itself has only one instruction: war, which creates a WAR file and specifies the location of the WEB-INF directory, the classes directory, the lib directory, and the unprocessed files such as JSPs. At this point you have the ability to generate a WAR file capable of being deployed by Tomcat.

The next step is to copy the file into the Tomcat deployment directory so that Tomcat can deploy the application. The following XML snippet copies the WAR file to the Tomcat deployment directory:

        

Enter the “copy-war” target right after the “package-war” target. To tie the “package-war” and “copy-war” targets together into a useful deploy function, create another target that simply calls the “package-war” target, followed by the “copy-war” target. Call that target “deploy”. Type the following into the build.xml file following the “copy-war” target:

  

The “deploy” target doesn’t contain any actual instructions. It merely calls other targets to perform work. You should also create an “undeploy” target so that you can undeploy an old version of the phonelist application before deploying a new version that results from changes you make to the code. Tomcat does not automatically delete the unpacked directory or the WAR archive when you shut it down. Furthermore, Tomcat does not detect changes to the WAR archive, so it does not automatically deploy new versions of an application when new WAR files are copied over old ones in the Tomcat deploy directory (except in the case of statically specified Tomcat applications in the server.xml configuration file). For both of these reasons, it is important to have an undeploy target in the Ant build.xml file. Add the following XML snippet after the “deploy” target:

            

Because you now have functioning targets in your Ant build file, you should change the default target. Currently you have the following in your build.xml file:

You should change it to the following:

At this point, you have a functioning build file with the ability to compile and deploy the application with one simple command line. The automated build will be essential during the development process. Look at the build.xml file included in the example code (phonelist-example-files.tgz or “.tbz2” or “.zip”) supplied with this article. The build.xml file is called build.xml.v1. It is essentially the same as the build.xml file you just created, but it has things in a different order that makes it easier to read and maintain. It also has comments that were omitted from the XML fragments in the step-by-step instructions you just followed.

You may choose to use the build.xml.v1 file as your base moving forward, but you do not have to. You will be adding XML code to the build.xml file in order to add tests. If you do choose to use it, be sure to rename it to build.xml after copying it to the ~/projects/phonelist directory.

Write Your Tests, Classes, and JSPs
Now proceed to writing your tests and your Java classes and JSPs for the phone list application. Since you are testing first, write the test before writing the code to implement the functionality. That way, you can be more certain that your test works and that the functionality being tested works. In order to test, you need to have a basic idea of how your application will work. The phone list application will have two pages:

  1. A page that displays the list of contacts with basic properties for each contact (The show list page also allows you to choose a contact to edit or choose contacts to delete.)
  2. A page that lets you enter or edit the details for a contact

Write the test for the page that displays the list of contacts. The test should do a couple of things:

  1. Verify that the “display contacts” page exists and returns content
  2. Verify that the “display contacts” page shows either a message that there are no contacts in the list or the list of contacts in the system

Go to the ~/projects/phonelist/src/test/com/abcinc/phonelist/test directory and create a file called ShowListTest.java. Create the shell for the ShowListTest class. The ShowListTest class will initially look like this:

package com.abcinc.phonelist.test;import junit.framework.TestCase;import junit.framework.TestSuite;public class ShowListTest extends TestCase {  public static void main(String args[]) {    junit.textui.TestRunner.run(suite());  }  public static TestSuite suite() {    return new TestSuite(ShowListTest.class);  }  public ShowListTest(String s) {    super(s);  }}

The class contains two methods and a constructor. The static main method allows you to run the class from the command line or via an Ant target call, which you will need to do in order to perform the testing from within an Ant target. The static suite method instantiates a new TestSuite object, which is passed to the static run method of the TestRunner class. You can instantiate a TestSuite object either with a TestCase-derived class or without one. If you pass in an instance of a subclass of TestCase, the TestSuite constructor will scan the TestCase-derived class for methods whose names begin with “test” that accept no arguments. The TestSuite object will automatically run all of the matching methods in the TestCase-derived class.

Now add in the tests. Place the following method after the ShowListTest constructor:

  public void testShowList() throws Exception {    WebConversation webConversation = new WebConversation();    String url = protocol + "://" + hostname + ":" + port + path;    WebRequest webRequest = new GetMethodWebRequest(url);    WebResponse webResponse = webConversation.getResponse(webRequest);    assertTrue("No content in phone list page", webResponse.getText().length() > 0);    WebTable webTable = webResponse.getTableStartingWith("Phone List");    assertNotNull("Phone List HTML table not found", webTable);    assertTrue("Phone List table has less than 4 rows", webTable.getRowCount() >= 4);  }

The testShowList method performs the following actions:

  1. Issues an HTTP GET request to the server at the location specified by the String object named “url”
  2. Obtains the HTTP response and stores it in a variable
  3. Verifies that the response contains more than zero bytes of data
  4. Verifies that the response contains an HTML table that has “Phone List” as its first non-blank text
  5. Verifies that the HTML table starting with “Phone List” has at least three rows of data

In addition to the code for the testShowList method, you also need to add in some import statements so that the HttpUnit classes you use in testShowList are recognized. Add the following code above the two “import junit” lines:

import com.meterware.httpunit.GetMethodWebRequest;import com.meterware.httpunit.TableCell;import com.meterware.httpunit.WebConversation;import com.meterware.httpunit.WebRequest;import com.meterware.httpunit.WebResponse;import com.meterware.httpunit.WebTable;

Also note that the testShowList method you just typed makes reference to a few variables that are not declared or defined: protocol, hostname, port, and path. So, add the following lines above the declaration of the main method but inside the ShowListTest class declaration:

  private static String protocol = "http";  private static String hostname = "localhost";  private static int port = 8080;  private static String path = "/phonelist/showList.do";

Your ShowListTest.java file should now look like the ShowListTest.java.v1 file supplied in the example files archive. Compare them to verify. The ShowListTest.java.v1 file contains comments that may simplify your understanding of how to build a TestCase-derived class.

You have now written the ShowListTest class, but you will not be able to compile or run it yet because the build.xml file contains no instructions pertaining to the tests. Add those instructions now to build.xml. First, you need to define a property that specifies the location of the HttpUnit distribution directory so that you can access the JAR files containing the classes. Add the following to the build.xml file right after the line defining the struts.install.dir property, which is located in the “init” target definition:

    

As usual, replace the value of the property with the location of the package on your system. Since you will be compiling Java classes that make use of the HttpUnit libraries, you will need to include the HttpUnit files in the classpath. Add the following lines to the build.xml file right after the line that says :

                                                          

You will use the test.lib.classpath property later to provide access to the HttpUnit libraries during compilation of your test classes. Now, add the following target to build.xml right after the target called “compile-classes”:

                            

The compile-tests target compiles the test classes located in the src/test directory of the phonelist project. It makes use of the test.lib.classpath property you previously defined to enable the compiler to resolve references to the HttpUnit libraries.

You need some targets to run the tests. Add the following snippet of XML to the build.xml file right after the definition of the deploy target:

                      

You define the test target so that you can run all tests with one command. You will have to keep adding to the depends attribute of the test target as you add more tests. The test-showlist target merely runs the showlist test. You may notice that the two targets you just defined rely on a target that does not yet exist as well as a property that does not yet exist. The non-existent target is post-compile-tests-init. The non-existent property is test.complete.classpath. In post-compile-tests-init, you define the test.complete.classpath property so that you can use it as a classpath in the test-showlist target. The reason you define the test.complete.classpath property separately in post-compile-tests-init rather than within the init target is that the test classes have not yet been compiled when the init target executes. Therefore, defining a path that included the test classes would not work because Ant expects path definitions to specify paths that exist. So add the post-compile-tests-init target. Add the following XML code to build.xml after the compile-tests target:

                                          

At this point, your build.xml file provides sufficient information for Ant to perform a build of the Web application. You have also told Ant how to perform a build of the tests and to execute the tests. You can copy build.xml.v2 from the example code archive and use it as your build.xml file. It should be the same as the build.xml file you just created, but it will have helpful comments that were excluded from the walk-through.

The Required XML Descriptors for Your Web App
Even though Ant knows what to do now, your Web application is still not quite ready for deployment yet. You need to add four files to the src/WEB-INF directory: struts-config.xml, tiles-defs.xml, validation.xml, and web.xml. The web.xml descriptor is mandatory for all Web applications. The other three XML descriptors are used by Struts. You should get the four files from the example code archive: struts-config.xml.v1, tiles-defs.xml.v1, validation.xml.v1, and web.xml.v1. Copy them and rename the copies to struts-config.xml, tiles-defs.xml, validation.xml, and web.xml, respectively. A brief explanation of each file follows.

struts-config.xml
The struts-config.xml descriptor configures the Struts framework for forms, actions, controllers, messages, and plug-ins. Version one of struts-config.xml is a bare-bones version that just configures the controller, the message resources definition file, the Tiles plug-in, and the Validator plug-in. The controller is the central switchboard: all HTTP requests for Struts actions end up going through it. The message resources definition file stores the text of prompts and errors to make localizing the application easy (i.e., change the content for a different language or country). The Tiles plug-in lets you compose multiple JSPs into a single view for display to the user, similar to what Web portals do. The Validator plug-in lets you do input validation so that you can screen form input for correct values.

tiles-defs.xml descriptor
The tiles-defs.xml descriptor configures the Tiles plug-in with the pages that the application will output. The file currently has no pages, so it is essentially an empty shell. (For further information on the Tiles plug-in, consult the Struts documentation.)

validation.xml descriptor
The validation.xml descriptor configures the Validator plug-in with the types and formats of data that you want to accept for the forms in your application. The file currently has no forms, so it is essentially an empty shell. (For further information on the Validator plug-in, consult the Struts documentation.)

Web.xml descriptor
The web.xml descriptor is the standard Web descriptor specified by the J2EE standard. The web.xml descriptor has a well-defined, standard structure. You can find the DTD that describes the structure at java.sun.com/dtd/web-app_2_3.dtd. In version one of the web.xml file supplied in the example code archive, several items are being configured:

  1. You set the name of the application to “Phone List Application”. This name is used for tools that manage WARs and tools that manage running Web applications in a servlet container like Tomcat.
  2. You define and configure the Struts Action servlet. The definition of the servlet allows us to refer to the Struts Action servlet later in the web.xml descriptor. The configuration turns on or off certain settings and specifies the locations of other Struts configuration files relative to the WAR root directory.
  3. You map the Action servlet you previously defined so that it handles all URLs that end in “.do”.
  4. You set the session timeout to 720 minutes (half a day) so that you don’t have to worry about losing session information because of inactivity (such as lunch breaks). This should be changed to something reasonable like 10 or 30 minutes for a real Web application.
  5. You set up the welcome file list so that requests for directories behave as if they’ve had one of the welcome files appended to the directory (e.g., a request for the directory/foo/bar would attempt to retrieve /foo/bar/index.jsp and /foo/bar/index.html first, before displaying a directory listing).
  6. You define the tag libraries that you use in your application and specify their locations. Tag libraries enable developers to embed tags in JSPs that call code in Java classes, possibly with parameters specified as attributes in the JSP tag or possibly with data contained within the tag. (For more information about tag libraries, consult the JSP tag library specification at java.sun.com/products/jsp/taglibraries.html.)

To build the Web application, change to the ~/projects/phonelist directory, then type “ant”. Start up Tomcat, then deploy the application to Tomcat by typing “ant deploy”. If all goes well, you should see something similar to Listing 1 in your Tomcat log.

The Tomcat log in a default installation is located at logs/localhost_log.2003-10-08.txt, except that 2003-10-08 is replaced by the actual date. Now run the test by typing “ant test”. You should see Listing 2.

The error is an indication that you have not yet set up the code to handle the “/showList.do” URI, which is what the test class requests from the Web server. To fix this problem, you need to write the code that enables the Web application to pass the test. You need to take the following steps:

  1. Add a section for manageForm in the section of struts-config.xml:
        

    The manageForm form keeps track of contacts that you select on the “show list” page and allows the delete action to retrieve the selections.

  2. Add sections for “/showList” and “/delete” in the section of struts-config.xml:
                                

    The “/showList” action instantiates the ShowListAction class and executes the code within, making available the “success” forward, which points to a tile called “showList”. The ShowListAction presumably forwards to the “success” forward in order to display generated results.

    The “/delete” action instantiates the DeleteAction class and executes the code within, making available the “success” forward, which points to a tile called “showList”. You do not have to implement the DeleteAction class for the “/delete” action yet. You need to define the “/delete” action right now, because you specify the “/delete” action as the action attribute for the tag in showList.jsp. Struts requires that you have at least a definition in struts-config.xml for any forms you use in your JSP pages that are defined with the Struts tag.

  3. Add a section for mainLayout in the section of tiles-defs.xml:
                    

    The mainLayout definition is a base template that other tile definitions can extend. Specifically, tiles that derive from mainLayout can override the titleString, body, and bodyParam1 tile variables. Child tiles can also introduce new tile variables.

  4. Add a section for showList in the section of tiles-defs.xml:
                

    The showList tile definition derives from the mainLayout tile definition but has a different titleString value and a different body page.

  5. Copy ManageForm.java.v1 from the example code archive and put it in src/WEB-INF/classes/com/abcinc/phonelist as ManageForm.java. The ManageForm class is a Struts form class. Struts form classes hold data from forms and let the application retrieve the data in an easy, object-oriented fashion. Struts form classes adhere to the JavaBean standard for accessors and mutators (get and set methods, respectively).
  6. Copy ShowListAction.java.v1 from the example code archive and put it in src/WEB-INF/classes/com/abcinc/phonelist as ShowListAction.java. The ShowListAction class is a Struts action class. Struts action classes serve as controller classes, which you can think of as switchboards. They direct the flow of control for the application and can sometimes also perform the business logic of the application for small applications where scalability is not a big issue.

    As an inelegant but quick hack, sample data was also set up in the ShowListAction class so that you have some data to work with. Ideally you would put the sample data in a separate file outside of the Web application so that the sample data did not get deployed with the application. The sample data population program would simply talk to a database and insert data into a blank or nearly blank database for the application you were building. For simplicity, there is a quick and dirty sample data population method in ShowListAction itself.

  7. Copy mainLayout.jsp.v1 from the example code archive and put it in src/Web as mainLayout.jsp. The mainLayout.jsp file is the base template for all of the other JSP files in the application. In a larger application, having a base template that all JSP files inherit from will be significantly more useful and will do much to promote consistency and uniformity across all pages of an application.
  8. Copy showList.jsp.v1 from the example code archive and put it in src/Web as showList.jsp. The showList.jsp file is a particular view for a particular operation. It inherits from mainLayout.jsp. It displays the list of phone contacts. It also contains a form that allows the user to select multiple contacts and then to delete those contacts. The reason the form action is a partial URL (“/delete.do”) rather than the full URL (http://localhost:8080/phonelist/delete.do) is that Struts automatically generates the full URL from the context-relative path (“/delete.do”). That is only if you use the Struts tag to define the form, however.
  9. Copy ContactBean.java.v1 from the example code archive and put it in src/WEB-INF/classes/com/abcinc/phonelist as ContactBean.java. The ContactBean class is a simple JavaBean that holds the properties for one contact. It provides a convenient method of storing the information on one contact and for passing such information between pieces of code as a single entity.
  10. Copy ContactDatabase.java.v1 from the example code archive and put it in src/WEB-INF/classes/com/abcinc/phonelist as ContactDatabase.java. The ContactDatabase class is a simple in-memory storage place for ContactBean instances. ContactDatabase.java rather than a relational database is used to keep this example simple.
  11. Copy Sequence.java.v1 from the example code archive and put it in src/WEB-INF/classes/com/abcinc/phonelist as Sequence.java. The Sequence class is a simple sequence counter that permits you to generate unique ID values for new ContactBean instances.

After the changes above, the struts-config.xml and tiles-defs.xml descriptors should match up with struts-config.xml.v2 and tiles-defs.xml.v2 from the example code archive. If they do not, you may use the files from the example code archive to continue. The versions of the files from the example code archive contain helpful comments, so use them going forward even if the files you typed up match with the supplied example code fairly well.

Put Your Application to the Test
Now test the revised version of the code to see if the application passes. Perform the following steps:

  1. Stop Tomcat by typing ./shutdown.sh in the bin directory in the Tomcat distribution.
  2. Change your working directory to ~/projects/phonelist.
  3. Type ant undeploy.
  4. Type ant deploy.
  5. Start Tomcat by typing ./startup.sh in the bin directory in the Tomcat distribution.

You can try out the application at http://localhost:8080/phonelist/showList.do. The application should display the list of contacts successfully.

Go back to the ~/projects/phonelist directory and type “ant test”. The showlist test should now pass because the URL it tests contains content and has four or more rows in a table with the title “Phone List”.

You still have to build the following functions: delete contacts, new contact, and edit contact. You also need to write the tests for those functions. In fact, you should write the tests before implementing the functionality.

In this article, Part I of “Build a Java Web Application Using HttpUnit and Test-driven Methodology”, you have learned the difference between programmatic API-driven testing and recorded macro-driven testing. You have also seen how HttpUnit complements the JUnit functionality to provide testing capability for Web applications, which ordinarily are difficult to test using only JUnit because they use HTTP calls rather than Java method calls. Finally, you have gone through a step-by-step setup of a test-driven environment and its accompanying functionality, as well as writing a test. In Part II, you will complete the example, performing more tests and filling out the functionality of the phone list application.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: