Unit Test Your Struts Application
The Problems With Unit Testing a Struts Application
What is Struts?
Struts is a successful web-application framework that uses
a central controller to control the page flow. The control logic is represented
by the Struts configuration file. Refer to struts.apache.org to learn more about Struts.
Below are a simple Struts configuration file and its related Action and Form, as well as its
JSP file.
struts-config.xml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
...
<form-beans>
<form-bean
name="simpleForm"
type="unittest.struts.SimpleForm"/>
</form-beans>
<action-mappings>
<action
path="/strutsTest"
type="unittest.struts.SimpleAction"
name="simpleForm"
scope="request">
<forward
name="succeed"
path="/result.jsp"/>
</action>
</action-mappings>
...
</struts-config>
//SimpleForm.java
package unittest.struts;
import org.apache.struts.action.ActionForm;
public class SimpleForm extends ActionForm {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//SimpleAction.java
package unittest.struts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import unittest.simple.ExternalInf;
public class SimpleAction extends Action {
//Get the name from the form and put it
//into request attribute
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
SimpleForm simpleForm = (SimpleForm)form;
ExternalInf inf = getExternalInf();
String name = simpleForm.getName();
if (name == null) {
name = "anonymous";
}
request.setAttribute("name", name +
inf.doSomeExtThing(10));
return mapping.findForward("succeed");
}
protected ExternalInf getExternalInf() {
ExternalInf inf = null;
//do some operation to get the
//external interface
//Use JNDI operation or something else
return inf;
}
}
//result.jsp
<%@ page contentType=
"text/html; charset=UTF-8"%>
<%@ taglib uri="/WEB-INF/struts-bean.tld"
prefix="bean" %>
<html>
<body>
<form name="form1">
The name is <input type="text" name="name"
value='<bean:write name="name"/>'/>
</form>
</body>
</html>
The Problems
Struts separates the view and controller from the old servlet approach. This helps
developers to implement each function more clearly, but it presents some problems
when doing unit tests. There are two approaches to do unit testing on Struts
applications:
Using Cactus to test a single Action class
It is possible to use Cactus to unit test the execute() method in the Action
class. Test case writers can continue to use the mock object and override
approaches. However, they have to construct the ActionMapping
object by themselves. ActionMapping is a Struts class that is
constructed by Struts itself, according to the content of the Struts configuration
file. To ask the test case writers to take on this responsibility is
not a good choice.
Below is a test case for SimpleAction.
//SimpleActionTest.java
package unittest.struts;
import org.apache.cactus.ServletTestCase;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.easymock.MockControl;
import unittest.simple.ExternalInf;
public class SimpleActionTest
extends ServletTestCase {
protected void setUp() throws Exception {
super.setUp();
}
protected void tearDown() throws Exception {
super.tearDown();
}
//test execute() method in SimpleAction
public void testExecute() {
//define the mock object
MockControl controller = MockControl.
createControl(ExternalInf.class);
final ExternalInf inf = (ExternalInf)
controller.getMock();
//define the behavior of mock object
inf.doSomeExtThing(10);
controller.setReturnValue("Great");
controller.replay();
//create SimpleAction class
//use override technology to bridge from
//mock object to the class
//to be tested
SimpleAction action = new SimpleAction() {
protected ExternalInf getExternalInf() {
return inf;
}
};
//prepare the SimpleForm
SimpleForm form = new SimpleForm();
form.setName("Dennis");
//prepare the ActionMapping
ActionMapping mapping=new ActionMapping();
mapping.addForwardConfig(new ActionForward(
"succeed",
"/result.jsp",
false));
try {
//do the test
ActionForward forward=action.execute(
mapping,
form,
request,
response);
//compare the result
assertNotNull(forward);
assertEquals("/result.jsp",
forward.getPath());
//verify the mock object
controller.verify();
} catch (Exception e) {
fail("Unexpected exception: " + e);
}
}
}
Using Cactus for an end-to-end test
In some circumstances, there is a requirement to do an end-to-end test in Struts.
For example, there is a test case to test whether a specific button in
the response HTML document is disabled or not, via a certain URL. There are many
limitations in this kind of test. Because the Struts framework controls the
whole flow, from accepting the URL to returning the response, there is no "join point" to interact with the test case. It is impossible to use mock objects and
overrides. This presents some difficulties for unit testing the
Struts application using external interfaces.
Both of these approaches have serious limitations. The first approach uses
traditional unit test technologies, but has limited scope. It can only
test the Action class, which is only part of the Struts framework. The second approach
has a larger scope and provides an end-to-end test solution, but cannot use the
traditional unit test technologies. It is very difficult to write test cases
without the help of these technologies.
Prev [1] [2] [3] [4] [5] Next