Tuesday 11 November 2014

Spring 3 - Create tests with MockWebApplication and MockWebApplicationContextLoader

Ok lets say one has to test his custom exception handler which he has defined in Spring 3, here is a way to do it :-

Define custom exception handler "APIExceptionHandler" -
public class APIExceptionHandler implements HandlerExceptionResolver {

    public ModelAndView resolveException(final HttpServletRequest request,final HttpServletResponse response, final Object handler, final Exception exception) {
    /**
     * return ModelAndView based on exception
    */

    }
}
Now create interface MockWebApplication:-

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MockWebApplication {
	/**
	 * The location of the webapp directory relative to your project.
	 * For maven users, this is generally src/main/webapp (default).
	 */
	String webapp() default "/web";
	/**
	 * The servlet name as defined in the web.xml.
	 */
	String name();
}

Create MockWebApplicationContextLoader as below:-

public class MockWebApplicationContextLoader extends AbstractContextLoader {
	/**
	 * The configuration defined in the {@link MockWebApplication} annotation.
	 */
	private MockWebApplication configuration;
	@SuppressWarnings("serial")
	public ApplicationContext loadContext(String... locations) throws Exception {
		// Establish the servlet context and config based on the test class's MockWebApplication annotation.
		final MockServletContext servletContext = new MockServletContext(new FileSystemResourceLoader());
		final MockServletConfig servletConfig = new MockServletConfig(servletContext, configuration.name());
		// Create a WebApplicationContext and initialize it with the xml and servlet configuration.
		final XmlWebApplicationContext webApplicationContext = new XmlWebApplicationContext();
		servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webApplicationContext);
		webApplicationContext.setServletConfig(servletConfig);
		webApplicationContext.setConfigLocations(locations);
		// Create a DispatcherServlet that uses the previously established WebApplicationContext.
		final DispatcherServlet dispatcherServlet = new DispatcherServlet() {
			protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
				return webApplicationContext;
			}
		};
		dispatcherServlet.setDetectAllHandlerMappings(true);
		final APIExceptionHandler aPIExceptionHandler = new APIExceptionHandler();
		// Add the DispatcherServlet (and anything else you want) to the context.
		// Note: this doesn't happen until refresh is called below.
		webApplicationContext.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
			public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
				beanFactory.registerResolvableDependency(DispatcherServlet.class, dispatcherServlet);
			beanFactory.registerResolvableDependency(HandlerExceptionResolver.class, aPIExceptionHandler);
				// Register any other beans here, including a ViewResolver if you are using JSPs.
			}
		});
		// Have the context notify the servlet every time it is refreshed.
		webApplicationContext.addApplicationListener(new SourceFilteringListener(webApplicationContext, new ApplicationListener<ContextRefreshedEvent>() {
			public void onApplicationEvent(ContextRefreshedEvent event) {
				dispatcherServlet.onApplicationEvent(event);
			}
		}));
		// Prepare the context.
		webApplicationContext.refresh();
		webApplicationContext.registerShutdownHook();
		// Initialize the servlet.
		dispatcherServlet.setContextConfigLocation("");
		dispatcherServlet.init(servletConfig);
		return webApplicationContext;
	}
	/**
	 * One of these two methods will get called before {@link #loadContext(String...)}.
	 * We just use this chance to extract the configuration.
	 */
	protected String[] generateDefaultLocations(Class<?> clazz) {
		extractConfiguration(clazz);
		return super.generateDefaultLocations(clazz);
	}
	/**
	 * One of these two methods will get called before {@link #loadContext(String...)}.
	 * We just use this chance to extract the configuration.
	 */
	protected String[] modifyLocations(Class<?> clazz, String... locations) {
		extractConfiguration(clazz);
		return super.modifyLocations(clazz, locations);
	}
	private void extractConfiguration(Class<?> clazz) {
		configuration = AnnotationUtils.findAnnotation(clazz, MockWebApplication.class);
		if (configuration == null)
			throw new IllegalArgumentException("Test class " + clazz.getName() + " must be annotated @MockWebApplication.");
	}
	protected String getResourceSuffix() {
		return "-context.xml";
	}
}

And finally the test:-
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={ "/spring/applicationContext-*.xml", "/spring/testContext-*.xml", "/spring/mockContext-*.xml"}, loader=MockWebApplicationContextLoader.class)
@MockWebApplication(name="os")
public class ExceptionHandlerTests { 
    /** Mock Request. */
    private MockHttpServletRequest request;
    /** Mock Response. */
    private MockHttpServletResponse response;
    @Autowired
    private DispatcherServlet servlet;
 
    /**
     * Setting up Mock up request, response
     * initialize controller and AnnotationMethodHandler
     * who will handler controller.
     */
    @Before
    public void setUp() {
        request    = new MockHttpServletRequest();
        response   = new MockHttpServletResponse();
        servlet.setDetectAllHandlerMappings(true);
    }

   @Test
    public void testException() throws Exception {
	   request    = new MockHttpServletRequest("GET","YOUR_URL");
	   response   = new MockHttpServletResponse();
	   servlet.service(request, response);
	   Document docXMLResponse = XMLUnit.buildControlDocument(response.getContentAsString());
	  /**
          /* Assertions.
          */
    }
  }

No comments:

Post a Comment