Spring3 and REST Integration(IV)Controller JUnit Test and Mock/Servlet
There are 4 different ways to JUnit our REST style JSON data Controller.
Servlet Mock
The first way is to use Servlet Mock. There are 2 base classes needed in our project
The Mock Web Application Loader class will be like these:
package com.sillycat.easyrestserver.core;
import javax.servlet.ServletException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SourceFilteringListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AbstractContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class MockWebApplicationContextLoader extends AbstractContextLoader {
MockWebApplication configuration;
static MockServletContext servletContext;
static MockServletConfig servletConfig;
static XmlWebApplicationContext webApplicationContext;
public ApplicationContext loadContext(
MergedContextConfiguration mergedConfig) throws Exception {
servletConfig = new MockServletConfig(servletContext,
configuration.name());
servletContext = new MockServletContext(configuration.webapp(),
new FileSystemResourceLoader());
webApplicationContext = new XmlWebApplicationContext();
webApplicationContext.getEnvironment().setActiveProfiles(
mergedConfig.getActiveProfiles());
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
webApplicationContext);
webApplicationContext.setServletConfig(servletConfig);
webApplicationContext.setConfigLocations(configuration.locations());
prepareWebApplicationContext();
return webApplicationContext;
}
public ApplicationContext loadContext(String... locations) throws Exception {
servletConfig = new MockServletConfig(servletContext,
configuration.name());
servletContext = new MockServletContext(configuration.webapp(),
new FileSystemResourceLoader());
webApplicationContext = new XmlWebApplicationContext();
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
webApplicationContext);
webApplicationContext.setServletConfig(servletConfig);
webApplicationContext.setConfigLocations(locations);
prepareWebApplicationContext();
return webApplicationContext;
}
private void prepareWebApplicationContext() throws ServletException {
@SuppressWarnings("serial")
final DispatcherServlet dispatcherServlet = new DispatcherServlet() {
protected WebApplicationContext createWebApplicationContext(
ApplicationContext parent) {
return webApplicationContext;
}
};
webApplicationContext
.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) {
beanFactory.registerResolvableDependency(
DispatcherServlet.class, dispatcherServlet);
}
});
webApplicationContext
.addApplicationListener(new SourceFilteringListener(
webApplicationContext,
new ApplicationListener<ContextRefreshedEvent>() {
public void onApplicationEvent(
ContextRefreshedEvent event) {
dispatcherServlet.onApplicationEvent(event);
}
}));
webApplicationContext.refresh();
webApplicationContext.registerShutdownHook();
dispatcherServlet.setContextConfigLocation("");
dispatcherServlet.init(servletConfig);
}
protected String[] generateDefaultLocations(Class<?> clazz) {
extractConfiguration(clazz);
return super.generateDefaultLocations(clazz);
}
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 we will use the mockwebapplication class to hold our annotation configuration:
package com.sillycat.easyrestserver.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MockWebApplication {
String webapp() default "src/main/webapp";
String name();
String[] locations() default "src/test/test-content.xml";
}
The test class will be like this:
package com.sillycat.easyrestserver.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerMapping;
import com.sillycat.easyrestserver.core.MockWebApplication;
import com.sillycat.easyrestserver.core.MockWebApplicationContextLoader;
import com.sillycat.easyrestserver.model.Company;
import com.sillycat.easyrestserver.model.Person;
/**
* mock the servlet and use servlet to test our controller
* servlet.service(mockRequest, mockResponse);
* can not mock the service or manager in controller
* @author karl
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = MockWebApplicationContextLoader.class)
@MockWebApplication(name = "easyrestserver", locations = "classpath:test-context.xml")
public class PersonControllerTest1 {
@Autowired
private DispatcherServlet servlet;
ObjectMapper mapper;
Person person;
MockHttpServletRequest mockRequest;
MockHttpServletResponse mockResponse;
@Before
public void setUp() throws ServletException, IOException {
MockitoAnnotations.initMocks(this);
mapper = new ObjectMapper();
person = new Person();
person.setCompany(new Company());
person.setId(1);
person.setPersonName("person1");
mockRequest = new MockHttpServletRequest();
mockRequest.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING,true);
mockResponse = new MockHttpServletResponse();
}
@Test
public void get() throws Exception {
mockRequest.setMethod("GET");
mockRequest.setRequestURI("/person/1");
mockRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
mockResponse = new MockHttpServletResponse();
servlet.service(mockRequest, mockResponse);
Person actualPerson = mapper.readValue(
mockResponse.getContentAsString(), Person.class);
Assert.assertEquals(actualPerson.getId(), person.getId());
}
@Test
public void add() throws Exception{
mockRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
mockRequest.setMethod("POST");
mockRequest.setRequestURI("/person");
String jsonPerson = mapper.writeValueAsString(person);
mockRequest.setContent(jsonPerson.getBytes());
servlet.service(mockRequest, mockResponse);
Assert.assertEquals(mockResponse.getStatus(), 200);
Person actualPerson = mapper.readValue(
mockResponse.getContentAsString(), Person.class);
Assert.assertEquals(actualPerson.getId(), person.getId());
}
}
The sample project named easyrestserver.
references:
spring 3 and rest
http://hi.baidu.com/luohuazju/blog/item/1fe358ea5da873c0d539c9b4.html
http://hi.baidu.com/luohuazju/blog/item/c380b67f9946421b28388ab7.html
http://hi.baidu.com/luohuazju/blog/item/3b68f609650a37dc3bc763c5.html
tcpdump on mac
http://support.apple.com/kb/HT3994
http://hi.baidu.com/luohuazju/blog/item/da079eb3de7109b2d9335aa5.html
mock test
http://www.iteye.com/topic/828513
http://stackoverflow.com/questions/9138555/spring-framework-test-restful-web-service-controller-offline-i-e-no-server-n
http://stackoverflow.com/questions/1401128/how-to-unit-test-a-spring-mvc-controller-using-pathvariable/2457902#2457902
http://ted-young.googlecode.com/svn/trunk/blog.spring-mvc-integration-testing/
https://github.com/springside/springside4/blob/master/examples/mini-service/src/test/java/org/springside/examples/miniservice/webservice/ws/AccountWebServiceTest.java
https://github.com/SpringSource/spring-test-mvc
http://elf8848.iteye.com/blog/875830
There are 4 different ways to JUnit our REST style JSON data Controller.
Servlet Mock
The first way is to use Servlet Mock. There are 2 base classes needed in our project
The Mock Web Application Loader class will be like these:
package com.sillycat.easyrestserver.core;
import javax.servlet.ServletException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.SourceFilteringListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.support.AbstractContextLoader;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class MockWebApplicationContextLoader extends AbstractContextLoader {
MockWebApplication configuration;
static MockServletContext servletContext;
static MockServletConfig servletConfig;
static XmlWebApplicationContext webApplicationContext;
public ApplicationContext loadContext(
MergedContextConfiguration mergedConfig) throws Exception {
servletConfig = new MockServletConfig(servletContext,
configuration.name());
servletContext = new MockServletContext(configuration.webapp(),
new FileSystemResourceLoader());
webApplicationContext = new XmlWebApplicationContext();
webApplicationContext.getEnvironment().setActiveProfiles(
mergedConfig.getActiveProfiles());
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
webApplicationContext);
webApplicationContext.setServletConfig(servletConfig);
webApplicationContext.setConfigLocations(configuration.locations());
prepareWebApplicationContext();
return webApplicationContext;
}
public ApplicationContext loadContext(String... locations) throws Exception {
servletConfig = new MockServletConfig(servletContext,
configuration.name());
servletContext = new MockServletContext(configuration.webapp(),
new FileSystemResourceLoader());
webApplicationContext = new XmlWebApplicationContext();
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
webApplicationContext);
webApplicationContext.setServletConfig(servletConfig);
webApplicationContext.setConfigLocations(locations);
prepareWebApplicationContext();
return webApplicationContext;
}
private void prepareWebApplicationContext() throws ServletException {
@SuppressWarnings("serial")
final DispatcherServlet dispatcherServlet = new DispatcherServlet() {
protected WebApplicationContext createWebApplicationContext(
ApplicationContext parent) {
return webApplicationContext;
}
};
webApplicationContext
.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) {
beanFactory.registerResolvableDependency(
DispatcherServlet.class, dispatcherServlet);
}
});
webApplicationContext
.addApplicationListener(new SourceFilteringListener(
webApplicationContext,
new ApplicationListener<ContextRefreshedEvent>() {
public void onApplicationEvent(
ContextRefreshedEvent event) {
dispatcherServlet.onApplicationEvent(event);
}
}));
webApplicationContext.refresh();
webApplicationContext.registerShutdownHook();
dispatcherServlet.setContextConfigLocation("");
dispatcherServlet.init(servletConfig);
}
protected String[] generateDefaultLocations(Class<?> clazz) {
extractConfiguration(clazz);
return super.generateDefaultLocations(clazz);
}
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 we will use the mockwebapplication class to hold our annotation configuration:
package com.sillycat.easyrestserver.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MockWebApplication {
String webapp() default "src/main/webapp";
String name();
String[] locations() default "src/test/test-content.xml";
}
The test class will be like this:
package com.sillycat.easyrestserver.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerMapping;
import com.sillycat.easyrestserver.core.MockWebApplication;
import com.sillycat.easyrestserver.core.MockWebApplicationContextLoader;
import com.sillycat.easyrestserver.model.Company;
import com.sillycat.easyrestserver.model.Person;
/**
* mock the servlet and use servlet to test our controller
* servlet.service(mockRequest, mockResponse);
* can not mock the service or manager in controller
* @author karl
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = MockWebApplicationContextLoader.class)
@MockWebApplication(name = "easyrestserver", locations = "classpath:test-context.xml")
public class PersonControllerTest1 {
@Autowired
private DispatcherServlet servlet;
ObjectMapper mapper;
Person person;
MockHttpServletRequest mockRequest;
MockHttpServletResponse mockResponse;
@Before
public void setUp() throws ServletException, IOException {
MockitoAnnotations.initMocks(this);
mapper = new ObjectMapper();
person = new Person();
person.setCompany(new Company());
person.setId(1);
person.setPersonName("person1");
mockRequest = new MockHttpServletRequest();
mockRequest.setAttribute(HandlerMapping.INTROSPECT_TYPE_LEVEL_MAPPING,true);
mockResponse = new MockHttpServletResponse();
}
@Test
public void get() throws Exception {
mockRequest.setMethod("GET");
mockRequest.setRequestURI("/person/1");
mockRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
mockResponse = new MockHttpServletResponse();
servlet.service(mockRequest, mockResponse);
Person actualPerson = mapper.readValue(
mockResponse.getContentAsString(), Person.class);
Assert.assertEquals(actualPerson.getId(), person.getId());
}
@Test
public void add() throws Exception{
mockRequest.setContentType(MediaType.APPLICATION_JSON_VALUE);
mockRequest.setMethod("POST");
mockRequest.setRequestURI("/person");
String jsonPerson = mapper.writeValueAsString(person);
mockRequest.setContent(jsonPerson.getBytes());
servlet.service(mockRequest, mockResponse);
Assert.assertEquals(mockResponse.getStatus(), 200);
Person actualPerson = mapper.readValue(
mockResponse.getContentAsString(), Person.class);
Assert.assertEquals(actualPerson.getId(), person.getId());
}
}
The sample project named easyrestserver.
references:
spring 3 and rest
http://hi.baidu.com/luohuazju/blog/item/1fe358ea5da873c0d539c9b4.html
http://hi.baidu.com/luohuazju/blog/item/c380b67f9946421b28388ab7.html
http://hi.baidu.com/luohuazju/blog/item/3b68f609650a37dc3bc763c5.html
tcpdump on mac
http://support.apple.com/kb/HT3994
http://hi.baidu.com/luohuazju/blog/item/da079eb3de7109b2d9335aa5.html
mock test
http://www.iteye.com/topic/828513
http://stackoverflow.com/questions/9138555/spring-framework-test-restful-web-service-controller-offline-i-e-no-server-n
http://stackoverflow.com/questions/1401128/how-to-unit-test-a-spring-mvc-controller-using-pathvariable/2457902#2457902
http://ted-young.googlecode.com/svn/trunk/blog.spring-mvc-integration-testing/
https://github.com/springside/springside4/blob/master/examples/mini-service/src/test/java/org/springside/examples/miniservice/webservice/ws/AccountWebServiceTest.java
https://github.com/SpringSource/spring-test-mvc
http://elf8848.iteye.com/blog/875830