Announcement Announcement Module
Collapse
No announcement yet.
Test Controllers Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Test Controllers

    Maybe this is a stupid question, but somebody have to pose stupid questions also...

    Is it at all possible to JUnit-test Spring controllers with JUnit but without HttpUnit and/or Cactus? And if it is, are there any "best practice"?

  • #2
    Yes, it's quite easy:

    1. create controller and populate its properties or constructor args in your JUnit test
    2. drive its public entry method using a mock request and response

    Comment


    • #3
      Simple example

      Here's a simple example. You'll need spring-mock.jar in your classpath.

      Controller Test:

      Code:
      package org.appfuse.web;
      
      import java.util.Map;
      
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      import junit.framework.TestCase;
      
      import org.apache.commons.logging.Log;
      import org.apache.commons.logging.LogFactory;
      import org.springframework.mock.web.MockHttpServletRequest;
      import org.springframework.mock.web.MockServletContext;
      import org.springframework.web.context.support.XmlWebApplicationContext;
      import org.springframework.web.servlet.ModelAndView;
      
      public class UserControllerTest extends TestCase {
          private static Log log = LogFactory.getLog(UserControllerTest.class);
          
          private XmlWebApplicationContext ctx;
      
          public void setUp() {
              String[] paths = {"/WEB-INF/applicationContext*.xml",
                                "/WEB-INF/action-servlet.xml"};
              ctx = new XmlWebApplicationContext();
              ctx.setConfigLocations(paths);
              ctx.setServletContext(new MockServletContext(""));
              ctx.refresh();
          }
      
      	public void testGetUsers() throws Exception {
      		UserController c = (UserController) ctx.getBean("userController");
      		ModelAndView mav = c.handleRequest((HttpServletRequest) null,
      				(HttpServletResponse) null);
      		Map m = mav.getModel();
      		assertNotNull(m.get("users"));
      		assertEquals(mav.getViewName(), "userList");
      	}
      }
      More examples can be found in AppFuse: http://tinyurl.com/6ansy

      HTH,

      Matt

      Comment


      • #4
        Wow!
        That's amazingly easy, can't wait to get back to work to try it.

        Thanks!

        Comment


        • #5
          Testing with EasyMock

          As Rod suggests, you can also test your controllers w/o talking to Spring's ApplicationContext at all. For instance, here's the same test using Easy Mock.

          Code:
          public class UserControllerEMTest extends TestCase {
              private static Log log = LogFactory.getLog(UserControllerEMTest.class);
              private MockControl control = null;
              private UserManager mockManager = null;
              private UserController c = null;
          
              protected void setUp() throws Exception {
                  control = MockControl.createControl(UserManager.class);
                  mockManager = (UserManager) control.getMock();
                  c = new UserController();
                  c.setUserManager(mockManager);
              }
          
              public void testGetUsers() throws Exception {
                  mockManager.getUsers();
                  control.setReturnValue(new ArrayList());
                  control.replay();
          
                  ModelAndView mav =
                      c.handleRequest((HttpServletRequest) null,
                                      (HttpServletResponse) null);
                  Map m = mav.getModel();
                  assertNotNull(m.get("users"));
                  assertEquals(mav.getViewName(), "userList");
                  control.verify();
              }
          }

          Comment


          • #6
            OK. Is this the easymock-package that you are using here?

            Comment


            • #7
              EasyMock

              Originally posted by kantorn
              OK. Is this the easymock-package that you are using here?
              Yes, from http://easymock.org. The code above uses EasyMock 1.1 (download).

              Comment


              • #8
                OK.

                Many thanks!

                Comment


                • #9
                  Running Cactus:jdbc.properties values are not visible...

                  I've got my web app built with spring 1.1.1 where Dao implemented with iBatis 2.x. DataSource is jdbc based, where all credentials placed into jdbc.properties file. Below is the fragment from the dataAccessContext-local.xml file. It works fine. All xml files are in /WEB-INF/classes folder. So by default, they are visible to the Tomcat classloader. All spring context files, including the mock context are listed in web.xml file. All dependent libraries in /WEB-INF/lib folder.

                  The problem appears if I use Cactus bundled with that web app. When I'm trying to deploy this test web app, I've got the error message, where all jdbc credentials are not substituted by the jdbc.properties values and treated by dbcp as is, so they look similar to the below:
                  ${jdbc.driverClassName}
                  ....
                  ${jdbc.password}

                  Question: Why jdbc.properties values are not visible when running with Cactus???

                  Thanks in advance!!!
                  ------------------------------------------------------------------------------


                  <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.Pr opertyPlaceholderConfigurer">
                  <property name="location"><value>classpath:jdbc.properties</value></property>
                  </bean>


                  <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
                  <property name="driverClassName"><value>${jdbc.driverClassNa me}</value></property>
                  <property name="url"><value>${jdbc.url}</value></property>
                  <property name="username"><value>${jdbc.username}</value></property>
                  <property name="password"><value>${jdbc.password}</value></property>
                  <property name="validationQuery"><value>select 1 from dual</value></property>
                  </bean>

                  Comment


                  • #10
                    Also, in terms of configuring mock HttpServletRequest objects, you can also subclass MockHttpServletRequest and provide your own setParameters(Map) method that simply calls addParameters -- since addParameters doesn't follow bean name conventions.

                    You can then configure mock http request objects in Spring instead of having request parameters set using Java code in a JUnit.

                    e.g.

                    Code:
                    <bean id="base-post" class="com.myco.test.MockRequest">
                      <property name="method"><value>POST</value></property>
                      <property name="session"><ref local="test-session"/></property>
                      <property name="servletPath"><value>/myapp</value></property>
                    </bean>
                    
                    <bean id="myPost" parent="base-post" singleton="false">
                      <property name="requestURI"><value>do-something.htm</value></property>
                      <property name="parameters">
                        <props>
                          <prop key="_target3">Find</prop>
                        </props>
                      </property>
                    </bean>

                    Comment


                    • #11
                      Testing error response

                      On the subject of mvc controller testing, is testing error conditions (situations where a controller generates errors) kind of messy? Verifying the happy-path is pretty simple with EasyMock or the cool Spring mock but when trying to peel the BindException, it seems fragile and very non-spring-like (TM). Am I missing something? Here is a sample test to make the point.
                      Code:
                        public void testBadUser&#40;&#41; throws Exception
                        &#123;
                          MockHttpServletRequest req = new MockHttpServletRequest&#40;&#41;;
                      
                          List expectedResults = new ArrayList&#40;&#41;;
                          ModelAndView result = null;
                          Map model = null;
                          auth = new Authentication&#40;USERNAME, PASSWORD&#41;;
                          errors = BindUtils.bind&#40;req, auth, "auth"&#41;;
                          acControl.expectAndReturn&#40;mockAC.isValidUser&#40;USERNAME&#41;, false&#41;;
                          acControl.replay&#40;&#41;;
                          result = ta.onSubmit&#40;req, null, auth, errors&#41;;
                          assertNotNull&#40;"got a mav", result&#41;;
                          assertEquals&#40;"got correct view name", FORM_VIEW, result.getViewName&#40;&#41;&#41;;
                      // UGLY STUFF BELOW...
                          BindException returnedException =
                            &#40;BindException&#41; result.getModel&#40;&#41;.get&#40;"org.springframework.validation.BindException.auth"&#41;;
                          assertNotNull&#40;returnedException&#41;;
                          assertEquals&#40;"got expected error count", 1, returnedException.getErrorCount&#40;&#41;&#41;;
                          List errors = returnedException.getAllErrors&#40;&#41;;
                          assertNotNull&#40;errors&#41;;
                          ObjectError error = &#40;ObjectError&#41; errors.get&#40;0&#41;;
                          assertEquals&#40;"got correct error", "Unknown username", error.getDefaultMessage&#40;&#41;&#41;;
                          assertNull&#40;
                            "no session setup",
                            req.getSession&#40;&#41;.getAttribute&#40;AuthInterceptor.USERNAME_ATTRIBUTE&#41;&#41;;
                          acControl.verify&#40;&#41;;
                      
                        &#125;
                      Can you say UCK?

                      Comment


                      • #12
                        good,i like it

                        Comment

                        Working...
                        X