Announcement Announcement Module
Collapse
No announcement yet.
Spring 2.5 Annotation based controller integration testing Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring 2.5 Annotation based controller integration testing

    Custom Web Context Loader

    Code:
    public class MockWebContextLoader extends AbstractContextLoader {
     
        public static final ServletContext SERVLET_CONTEXT = new MockServletContext("/WebContent", new FileSystemResourceLoader());
     
        private final static GenericWebApplicationContext webContext = new GenericWebApplicationContext();
     
        protected BeanDefinitionReader createBeanDefinitionReader(final GenericApplicationContext context) {
            return new XmlBeanDefinitionReader(context);
        }
     
        public final ConfigurableApplicationContext loadContext(final String... locations) throws Exception {
     
            SERVLET_CONTEXT.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webContext);
            webContext.setServletContext(SERVLET_CONTEXT);
            createBeanDefinitionReader(webContext).loadBeanDefinitions(locations);
            AnnotationConfigUtils.registerAnnotationConfigProcessors(webContext);
            webContext.refresh();
            webContext.registerShutdownHook();
            return webContext;
        }
     
        public static WebApplicationContext getInstance() {
            return webContext;
        }
     
        protected String getResourceSuffix() {
            return "-context.xml";
        }
     
    }
    Base Test Class to be inherited by the test controllers

    Code:
    package com.spring.sample.controller.integrationtests;
    
    import java.util.Map;
    
    import javax.servlet.ServletException;
    
    import junit.framework.TestCase;
    
    import org.junit.runner.RunWith;
    import org.springframework.mock.web.MockHttpServletRequest;
    import org.springframework.mock.web.MockHttpServletResponse;
    import org.springframework.mock.web.MockServletConfig;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.servlet.DispatcherServlet;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(loader=MockWebContextLoader.class, locations={"/config/spring-servlet.xml"})
    public abstract class AbstractControllerTestSupport extends TestCase {
     
        private static DispatcherServlet dispatcherServlet;
         
        @SuppressWarnings("serial")
        public static DispatcherServlet getServletInstance() {
                if(null == dispatcherServlet) {
                    dispatcherServlet = new DispatcherServlet() {
                        protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
                            return MockWebContextLoader.getWebAppContext();
                        }
                    };
                    try {
                      dispatcherServlet.init(new MockServletConfig());
                    } catch (ServletException se) {
                      // log exception
                    }
                }
            return dispatcherServlet;
        }
     
        protected MockHttpServletRequest mockRequest(String method, String uri, Map<String ,String> params) {
            MockHttpServletRequest req = new MockHttpServletRequest(method, uri);
            if (params != null) {
              for(String key : params.keySet()) {
                  req.addParameter(key, params.get(key));
              }
            }
            return req;
        }
     
        protected MockHttpServletResponse mockResponse() {
            return new MockHttpServletResponse();
        }
    }
    Controller


    Code:
    package com.spring.sample.controller;
    
    import javax.annotation.Resource;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import com.spring.sample.model.YGUser;
    import com.spring.sample.service.YGUserService;
    
    @Controller("userController")
    public class UserController {
      
      @Resource(name="ygUserService")
      YGUserService userService;
      
      @ModelAttribute
      public void initUser(@ModelAttribute("user") YGUser user) {}
      
      @RequestMapping(method = RequestMethod.POST, value = "/registerUser.do")
      public String registerUser(@ModelAttribute("user") YGUser ygUser, Model model) {
        userService.save(ygUser);
        return "registrationSuccessJsonView";
      }
    }
    Test Controller

    Code:
    package com.spring.sample.controller.integrationtests;
    
    import java.io.IOException;
    
    import javax.annotation.Resource;
    import javax.servlet.ServletException;
    
    import org.junit.Test;
    import org.springframework.mock.web.MockHttpServletRequest;
    import org.springframework.mock.web.MockHttpServletResponse;
    
    import com.spring.sample.controller.UserController;
    
    public class TestUserController extends AbstractControllerTestSupport {
    
      @Resource(name="userController")
      UserController userController;
      
      @Test
      public void testRegisterUser() throws IOException, ServletException {
        YGUser testUser = new YGUser("test user 1", "[email protected]", "test.user");
      
        MockHttpServletRequest currentRequest = mockRequest("POST", "/registerUser.do", null);
        
        MockHttpServletResponse currentResponse = mockResponse();
        
        getServletInstance().service(currentRequest, currentResponse);
        
        MockHttpServletResponse returnedResponse = currentResponse;
        
        assertNotNull(returnedResponse);
      }
    }
    spring-servlet.xml

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans 
                     http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                   http://www.springframework.org/schema/context 
                     http://www.springframework.org/schema/context/spring-context-2.5.xsd" >
    
      <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
      <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
    
      <context:component-scan base-package="com.spring.sample.controller" />
      <context:component-scan base-package="com.spring.sample.service" />
      
                        
                        <!-- VIEW CONFIGURATION -->
      <!-- A XmlViewResolver having JSON views bean definitions in views.xml -->
      
      
      
    </beans>

    Actually I dont have the presentation tier(JSP Pages) ready.And I want to test my controller to verify that it is behaving correctly.
    As can be seen in controller the YGUser model object will be populated from the values in the html form(will be generated by the spring form tag library) in the JSP page.

    In such a case I will receive a correctly populated YGUser instance in the controller when actually hitting the request
    "..../registerUser.do"

    I dont want to use @RequestParam annotation for the method argument YGUser.

    When debugging the test controller, I am receiving a YGUser instance but with the instance properties values as null because of the @ModelAttribute annotated method being called first.

    I am testing the Spring 2.5 annotation based controllers first time.Above is what I could find on the web for doing the same.

    I am stuck at how to proceed ahead, in the sense that how to provide the appropriate populated model objects to MockHttpServletRequest
    and verify that I am getting the expected response, the view, etc?

    For e.g. in my testRegisterUser() method, I have created a "testUser" instance (which in real scenario would be created by Spring implicitly
    because of the YGUser model attribute bound to spring's form tag in the JSP page).

    But how to pass the same to my test context so that actually when the registerUser() method in the controller is called the YGUser is populated with the test values I set in the test method.

    Similarly If I want to validate my YGUser object before saving to the database using Spring Validation API then how to test it in the context mentioned above.

    Please help in making it work.
    Also, any comments on incorrect usage of API or improving the code using API in an efficient way would be really helpful.

    Thanks,
    Jignesh

  • #2
    Jignesh,

    Wow. That's a lot of code and configuration you've supplied. I unfortunately don't have the time right now try it all out and debug it for you, but I can give a few quick pointers.
    • Have you considered simply unit testing your UserController? That should be a rather simple task if you create a dynamic mock (e.g., EasyMock) or stub for your YGUserService and manually inject it into the controller using ReflectionTestUtils.
    • On the other hand, if you have compelling reasons for writing an out-of-container integration test for your controller, you might be interested in following a related JIRA issue.
    • Regarding the error you're seeing, 1) it doesn't appear that you even need the initUser() method in your controller; 2) in testRegisterUser() you supply null as the request parameters to the mockRequest() method. So that could well explain why all of your instance property values are null.

    Hope this helps,

    Sam

    Comment

    Working...
    X