|
Helping ordinary people create extraordinary websites! |
StrutsTestCase Simplifies the Development ProcessBy Sunil Patil2005-04-28
Using the Mock Object Approach Mock testing is a popular approach for unit testing applications. Refer to Resources if you're new to the mock testing approach and want to know more. Setup for the mock approach To use the mock approach, we will have to make a few changes to the sample application. We will start by writing a mock test: 1. Add strutstest-2.1.•.jar and junit3.8.1.jar to the classpath 2. Add the WEB-INF folder to your classpath 3. Create a MockLoginTestAction class that extends the MockStrutsTestCase class 4. Run the unit test case Now that you have set up the environment, you can start writing the unit test cases. Blank user name or password First, you want to verify that if the user leaves the user name or password fields blank, he is shown an appropriate error message and redirected to the login page. Create the testLoginActionFormError() method in the MockLoginTestAction class, as shown in Listing 6: Listing 6. The testLoginActionFormError() method public void testLoginActionFormError()throws Exception{
The first thing you need to do when writing the STC test case is to tell STC which ActionMapping class you want to test. In the sample, we want to test LoginAction, which is mapped to the "/login" path in struts-config.xml, so we will have to call setRequestPathInfo("/login"). By default, STC looks in the /WEB-INF/ folder for struts-config.xml. If this file is not in the classpath, you can call setConfigFile() with the complete path of your struts-config.xml file.
Now you're ready to execute the test case. A call to the actionPerform() method executes the test case by passing control to the Struts framework. Once control has returned from actionPeform(), you can test the assumptions by calling the verifyXXX() method. In the sample application, we want to test whether the LoginAction mapping, when called without a user name and password, redirects the user to the login page with ActionErrors for username.required and password.required. The verifyInputForward() method checks that, as a result of this transaction, the user is redirected to the page indicated by the input attribute in our action mapping, which in this case is Login.jsp. You can call verifyActionErrors() with the array of Strings indicating which ActionErrors should be set in request scope as a result of this transaction. We are expecting that username.required, password.required, and ActionErrors would be set, so we create an array of Strings with these errors and pass them to the verifyActionErrors() method. How the STC mock approach works ActionServlet is a controller servlet in the Struts framework. When a container gets a request, it passes the request to ActionServlet, which does all the request processing. The basic idea behind STC is to create the object of ActionServlet yourself, instead of allowing the container to create it, then calling its appropriate methods. ActionServlet requires ServletContext and ServletConfig objects at the time of initialization, and requires objects of HttpServletRequest and HttpServletResponse at the time of request processing. STC creates mock objects of these classes and passes them on to Struts. MockStrutsTestCase is a JUnit test case that extends the junit.framework.TestCase class, so the setup() method is executed for every test case. In the setup() method of MockStrutsTestCase, STC creates objects for ActionServlet and other required mock objects. When you call the setRequestPathInfo() or addRequestParameter() methods, the appropriate methods of a mock HttpServletRequest object are called. In the mock implementation of HttpServletRequest, it stores this information to the appropriate setup state. So if you call addRequestParameter("name","value"), the mock HttpServletRequest object stores it, and when Struts calls request.getParameter("name"), it returns "value" as the value. Once you are done initializing HttpServletRequest properly, you can pass control to Struts by calling the actionPerform() method. The actionPerform() method calls the doPost() method on ActionServlet, passing the mock implementation of HttpServletRequest and HttpServletResponse. In the doPost() method of ActionServlet, the request is processed like any other Struts request, except that request processing is stopped just before the ActionForward JSP component is executed. In this stage, the state of the mock objects is changed to note things like what ActionErrors or ActionMessages were saved, or what was the resulting ActionForward. Once control returns from the actionPerform() method, you can check our various assumptions by calling the appropriate verifyXXX() method, which checks the state of mock objects. Test disabled user There is one problem with the isUserDisabled() method of the LoginActionForm class. In this method, we query the USERDISABLED table to find out whether the user account is disabled. In the current context, we do not want to waste time setting up and querying the database. Keep in mind that our goal is to check the Struts part of the application, not the database interaction code. To test the database interaction code, you could choose from several available tools, such as DBUnit. The best approach for this situation would be to create a subclass of the LoginActionForm class and override the isUserDisabled() method in it. This method should return true or false based on the input parameter values. Say in this case, the method will always return true unless you call it with disabledUser as an input parameter. Now this method should be used only during the unit testing phase, and the main LoginActionForm should not know about it. For this type of requirement, I created STCRequestProcessor, which extends RequestProcessor. It allows you to insert a mock implementation of your Action and ActionForm class. To use STCRequestProcessor, change struts-config.xml, as shown in Listing 7: Listing 7. The struts-config.xml file <controller>This line instructs Struts to use STCRequestProcessor.java as RequestProcessor. Don't forget to remove these lines when you deploy your application in a container. Next up is to create a mock class for LoginActionForm, as shown in Listing 8: Listing 8. The MockLoginActionForm.java class public class MockLoginActionForm extends LoginActionForm {
The isUserDisabled() method checks if the user name is "disableduser". If so, only true should be returned; otherwise, we get false.
Next, create a test case to test for a disabled user, as shown in Listing 9: Listing 9. The testDisabledUser() method public void testDisabledUser()throws Exception{
The STCRequestProcessor.addMockActionForm() method inserts MockLoginActionForm as a mock implementation for LoginActionForm. The addRequestParameter() method sets the user name and password request parameters. Once control returns from actionPerform(), we verify that the user is redirected to the input page with the user.disabled error message by calling verifyActionErrors().
Testing the invalid login Test cases test the business logic inside the execute() method of the LoginAction class. The execute() method calls the isValidUser() method of the same class, which in turn queries the USER table to find out if the user name and password combination is valid. Now, because we do not want to query the real database during the unit testing phase, we will create a mock subclass of the LoginAction class, overriding the isValidUser() method, as shown in Listing 10: Listing 10. The MockLoginAction.java class public class MockLoginAction extends LoginAction {
The MockLoginAction class' isValidUser() method returns true if the user name is "ibmuser" and the password is "ibmpassword". Call the STCRequestProcessor.addMockAction() method to insert MockLoginAction in place of LoginAction, as shown in Listing 11:
Listing 11. The testInvalidLogin() method public void testInvalidLogin()throws Exception{
In this test case, mock implementations of LoginAction and LoginActionForm are inserted to avoid database queries, followed by setting the user name and password parameters. After control returns from actionPerform(), we check whether the user is redirected to the login page with "invalid.login" as an error message.
Testing the valid login It's time to verify that when the user enters the correct user name and password, he is greeted with the success page, as shown in Listing 12: Listing 12. testLoginActionFormError public void testValidLogin() throws Exception{
This segment first sets the user name as "ibmuser" and the password as "ibmpassword" in the request parameter, then calls actionPerform(). Once the actionPerform() method is executed, it checks that the user is redirected to the success page by calling the verifyForward() method. Also call the verifyNoActionErrors() method to verify that no ActionErrors occurred during this transaction.
Tutorial Pages: » Save Time by Using STC's Mock and Cactus Testing Approaches » Sample Application » Using the Mock Object Approach » Advantages and Disadvantages of Mocking » Cactus Approach » Cactus Pros and Cons » Conclusion » Resources First published by IBM DeveloperWorks |
|