Browse DevX
Sign up for e-mail newsletters from DevX


StrutsTestCase: Drilled-down Testing for Struts-based Java Apps : Page 3

This article demonstrates both mock testing and in-container testing of Web-based applications built with the Struts Model-View-Controller framework. StrutsTestCase, a small testing library that builds on top of the JUnit testing framework, allows you to perform both testing methodologies on Struts applications.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Running Mock Tests
Change to the ~/projects/phonelist-strutstestcase directory and type: ant run-mock-tests. That will run all of the mock tests, which do not require a running Tomcat server. The mock approach's speed advantage is pretty clear. Not only is it faster to test in a static sense, but more importantly, the development cycle over time is much faster because you do not need to perform the following sequence of steps each time you test a modified piece of code:
  1. Shut down the servlet container (Tomcat or other container).
  2. Delete the files in the servlet container's deployment directory.
  3. Copy the new files to the servlet container's deployment directory.
  4. Restart the servlet container.

All you have to do is let Ant compile the changed classes, and then you jump straight into executing the code. If you look at the build.xml file, you see that the run-mock-tests target invokes two tests: edit and save. The targets are called test-mock-edit and test-mock-save, respectively.

The "edit" test is fairly simple. The following is the bare class definition of the TestEditAction.java file located in the src/test/com/abcinc/phonelist/test/mock subdirectory of the ~/projects/phonelist-strutstestcase directory:

public class TestEditAction extends MockStrutsTestCase { public TestEditAction(String testName) { super(testName); } public static void main(String args[]) { junit.textui.TestRunner.run(suite()); } public static TestSuite suite() { return new TestSuite(TestEditAction.class); } public void testEdit() { setRequestPathInfo("/showList"); actionPerform(); setRequestPathInfo("/edit"); addRequestParameter("id", "1"); actionPerform(); EditForm editForm = (EditForm)getActionForm(); Integer id = editForm.getId(); assertEquals("id value", id, new Integer(1)); verifyForward("success"); } }

The file you downloaded contains import statements and comments at the top (for brevity, I omitted the comments). Note that the class extends MockStrutsTestCase. All StrutsTestCase tests inherit either from MockStrutsTestCase or CactusStrutsTestCase. You could easily turn it into an in-container test by changing MockStrutsTestCase to CactusStrutsTestCase.

Let's examine the testEdit() method. (My previous HttpUnit articles explain the purposes of the constructor, main, and suite() methods.) The general pattern for a StrutsTestCase test is the following:

  1. setRequestPathInfo(): sets the action path that will be executed subsequently. The parameter you pass in is the context-relative URI.
  2. addRequestParameter(): adds one or more parameters to the request object. These are passed to the Struts action you call as if they came from the Web browser.
  3. setActionForm(): sets the ActionForm object with an initialized ActionForm containing values you define.
  4. actionPerform(): calls the Struts action specified in setRequestPathInfo().
  5. verifyNoActionErrors(): verifies that the Struts action did not flag any errors.
  6. getActionForm(): gets the ActionForm object and checks values in the ActionForm against known values.
  7. assertXxx(): checks any assertions that you wish.
  8. verifyForward(): verifies that the ActionForward is the right value.

You can repeat the cycle as many times as you wish within a method of a TestCase class. In the example above, you can see that the "/showList.do" action is called first. The ".do" extension is optional. If you do not specify it, StrutsTestCase will automatically append the ".do" extension. After you do that, you then call the "/edit.do" action, passing in a request parameter named "id" with a value of "1".

Note that you can access the ActionForm object (in this case, a subclass of ActionForm named EditForm). This is something that would not be possible with black box testing. You can get access to not only ActionForm but also the ActionServlet controller, the HttpSession object, and other objects that are accessible only within the servlet container. As a result, you have more control over your testing and can test more detailed conditions with the unit testing capability StrutsTestCase offers.

Take a look at the TestSaveAction located in the same directory as TestEditAction. It has very similar functionality to TestEditAction, but has more meat to it.

As you can see, mock testing is fairly straightforward. It can also be limiting in some circumstances, as shown by the lack of support for JNDI and some other services provided by the servlet container. Mock objects are not intended to replicate the servlet container completely. The good news is that StrutsTestCase makes it easy to change a mock test to an in-container test.

Running In-container Tests
To run the tests using an in-container approach, you normally need to perform the following additional setup steps (which I have already done in the download file):

  1. Add four blocks of data to the web.xml file (see below).
  2. Make sure that a cactus.properties file exists in the classpath when you execute the Cactus test.
  3. Copy the Cactus and StrutsTestCase JAR files to the WEB-INF/lib directory of the packed WAR file containing your application.
  4. Copy the cactus.properties file to the WEB-INF/classes directory (or somewhere in the classpath).
  5. Copy the test classes (e.g., TestDeleteAction.java) to the WEB-INF/classes directory at the right place in the hierarchy.

The following two blocks of servlet configurations are required in web.xml:

<!— ServletRedirector Servlet Configuration —> <servlet> <servlet-name>ServletRedirector</servlet-name> <servlet-class> org.apache.cactus.server.ServletTestRedirector </servlet-class> </servlet> <!— ServletTestRunner Servlet Configuration —> <servlet> <servlet-name>ServletTestRunner</servlet-name> <servlet-class> org.apache.cactus.server.runner.ServletTestRunner </servlet-class> </servlet>

Additionally, you need to add two corresponding servlet mappings in web.xml:

<!— ServletRedirector Servlet Mapping —> <servlet-mapping> <servlet-name>ServletRedirector</servlet-name> <url-pattern>/ServletRedirector</url-pattern> </servlet-mapping> <!— ServletTestRunner Servlet Mapping —> <servlet-mapping> <servlet-name>ServletTestRunner</servlet-name> <url-pattern>/ServletTestRunner</url-pattern> </servlet-mapping>

The cactus.properties file should contain a property called cactus.contextURL at minimum. Normally, the line should say something like cactus.contextURL=http://localhost:8080/test, where localhost is the name of the host running Tomcat (many times this is the literal string "localhost") and /test is the context path of the Web application. In this case, the context path of the Web application is /phonelist-strutstestcase. So the full value of cactus.contextURL should be http://localhost:8080/phonelist-strutstestcase. I put the cactus.properties file in the ~/projects/phonelist-strutstestcase/src/test/com/abcinc/phonelist/test/container directory, but you can put it anywhere in the classpath.

To learn how the JAR files, cactus.properties file, and test classes are copied and placed in the packed WAR file, consult the build.xml file—specifically, the copy-unprocessed-web-files, copy-unprocessed-test-files, and package-war targets.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date