I write JUnit tests for some Spring MVC Controllers. The initialization of the JUnit test is common for all my Controllers tests, so I wanted to create an abstract class that does this initialization.
Thus, I created the following code:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:spring/applicationContext-test.xml", "classpath*:spring/spring-mvc-test.xml" })
@Transactional
public abstract class AbstractSpringMVCControllerTest<T> {
@Autowired
protected ApplicationContext applicationContext;
protected MockHttpServletRequest request;
protected MockHttpServletResponse response;
protected HandlerAdapter handlerAdapter;
protected T controller;
@SuppressWarnings("unchecked")
@Before
public void initContext() throws SecurityException, NoSuchFieldException {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handlerAdapter = applicationContext.getBean(AnnotationMethodHandlerAdapter.class);
// Does not work, the problem is here...
controller = applicationContext.getBean(T);
}
}
The idea is to create, for each controller I want to test a JUnit test class that extends my AbstractSpringMVCControllerTest. The type given in the extends declaration is the class of the Controller.
For example, if I want to test my AccountController, I will create the AccountControllerTest class like that:
public class AccountControllerTest extends AbstractSpringMVCControllerTest<AccountController> {
@Test
public void list_accounts() throws Exception {
request.setRequestURI("/account/list.html");
ModelAndView mav = handlerAdapter.handle(request, response, controller);
...
}
}
My problem is located in the last line of the initContext() method of the abstract class. This abstract class declares the controller object as a T object, but how can say to the Spring Application Context to return the bean of type T?
I’ve tried something like that:
Class<?> controllerClass = this.getClass().getSuperclass().getDeclaredField("controller").getType();
controller = (T) applicationContext.getBean(controllerClass);
but controllerClass returns the java.lang.Object.class class, not AccountController.class.
Of course, I can create a public abstract Class<?> getControllerClass(); method, which will be overriden by each JUnit Controller test class, but I prefer to avoid this solution.
So, any idea?
This is possible if your subclasses of
AbstractSpringMVCControllerTestbindTat compile time. That is, you have something likerather than
I’m guessing you probably have the former. In this case, the type of
Tis erased from theClassobject forAbstractSpringMVCControllerTestat runtime, but theClassobject forDerpControllerTestdoes provide a way to know whatTis, since it boundTat compile time.The following classes demonstrate how to access the type of
T:Super.java
Sub.java
Test.java