Isolated Controller Test can't instantiate Pageable - spring-mvc

I have a Spring MVC Controller which uses Pagination Support of Spring-Data:
#Controller
public class ModelController {
private static final int DEFAULT_PAGE_SIZE = 50;
#RequestMapping(value = "/models", method = RequestMethod.GET)
public Page<Model> showModels(#PageableDefault(size = DEFAULT_PAGE_SIZE) Pageable pageable, #RequestParam(
required = false) String modelKey) {
//..
return models;
}
}
And I'd like to test the RequestMapping using the nice Spring MVC Test Support. In order to keep these tests fast and isolated from all the other stuff going on, I do not want to create the complete ApplicationContext:
public class ModelControllerWebTest {
private MockMvc mockMvc;
#Before
public void setup() {
ModelController controller = new ModelController();
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void reactsOnGetRequest() throws Exception {
mockMvc.perform(get("/models")).andExpect(status().isOk());
}
}
This approach works fine with other Controllers, that do not expect a Pageable, but with this one I get one of these nice long Spring stacktraces. It complains about not being able to instantiate Pageable:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.data.domain.Pageable]: Specified class is an interface
at
.... lots more lines
Question: How do I change my test so the magic No-Request-Parameter-To-Pageable conversion happens properly?
Note: In the actual application everything is working fine.

Original answer:
The problem with pageable can be solved by providing a custom argument handler. If this is set you will run in a ViewResolver Exception (loop). To avoid this you have to set a ViewResolver (an anonymous JSON ViewResolver class for example).
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.setViewResolvers(new ViewResolver() {
#Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return new MappingJackson2JsonView();
}
})
.build();
Updated (2020):
It is not necessary to add the ViewResolver anymore.
Regarding the parallel answer:
It does not solve the problem for the original question to have this test running without ApplicationContext and/or friends.

Just add #EnableSpringDataWebSupport for test. Thats it.

For spring boot simply adding the ArgumentResolvers solved for me:
From code which triggered the error:
this.mockMvc = MockMvcBuilders.standaloneSetup(weightGoalResource).build();
To this, which works:
this.mockMvc = MockMvcBuilders.standaloneSetup(weightGoalResource)
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build();

Related

Mockito failure: Actually, there were zero interactions with this mock

I'm trying to test a spring rest controller class using JUnit, Mockito, Spring test and Spring Security test. The following is my rest controller class for which i'm performing the test;
#RestController
public class EmployeeRestController {
#Autowired
private EmployeeService employeeService;
#PreAuthorize("hasAnyRole('ROLE_EMPSUPEADM')")
#RequestMapping(value = "/fetch-timezones", method = RequestMethod.GET)
public ResponseEntity<List<ResponseModel>> fetchTimeZones() {
List<ResponseModel> timezones = employeeService.fetchTimeZones();
return new ResponseEntity<>(timezones, HttpStatus.OK);
}
}
The following is my test class;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringConfiguration.class})
#WebAppConfiguration
public class EmployeeRestControllerUnitTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Mock
private EmployeeService employeeService;
#InjectMocks
private EmployeeRestController employeeRestController;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
Mockito.reset(employeeService);
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
}
#Test
#WithMockUser(roles = {"EMPSUPEADM"})
public void testFetchTimezones() {
try {
mockMvc.perform(get("/fetch-timezones"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$", hasSize(4)));
verify(emploeeService, times(1)).fetchTimeZones();
verifyNoMoreInteractions(employeeService);
} catch (Exception e) {
e.printStackTrace();
}
}
}
I made the above test class by refering many tutorials. The problem is i'm not able to understand everything clearly. so, i'm having the following doubts.
I'm creating a mock of EmployeeService and injecting it into EmployeeRestController using #InjectMocks, then why i'm getting the following failure;
Wanted but not invoked:
careGroupService.fetchTimeZones();
-> at com.example.api.test
.restcontroller.EmployeeRestControllerUnitTest
.testFetchTimezones(EmployeeRestControllerUnitTest.java:73)
Actually, there were zero interactions with this mock.
How does MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); works exactly.
I know that MockMvcBuilders.standaloneSetup(employeeRestController) is for testing individual controller classes and spring configuration will not be available for this method. How can we provide spring configuraton for this method, is it possible.
Finally, how does this piece of code: Mockito.reset(employeeService); works.
1) you do verify for
verify(emploeeService, times(1)).fetchTimeZones();
but you didn't setup mock behaviour for it (before you call mockMvc.perform(get("/fetch-timezones"))).
List<ResponseModel> timezones = new ArrayList<>();
when(emploeeService.fetchTimeZones()).thenReturn(timezones );
2) create MockMvc from context. mockmvc emulates web container, use mock for all where is possible but supports http call and gave the possibility to call controller.
It stands up the Dispatcher Servlet and all required MVC components,
allowing us to test an endpoint in a proper web environment, but
without the overhead of running a server.
3) when you use:
#MockBean private EmployeeService employeeService;
you override real service. remove it from test class real service will be used in testing. Instead of use #Mock use #MockBean. #MockBean it's override by container, with #Mock you need to inject this into controller by reflection
or without spring boot with reflection:
#Before
public void init() {
MockitoAnnotations.initMocks(this);
Mockito.reset(employeeService);
mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.build();
EmployeeRestController employeeRestController=
webAppContext.getBean(EmployeeRestController.class);
ReflectionTestUtils.setField(employeeRestController,
"employeeService",
employeeService);
}
4) Mockito.reset(employeeService);- you reset all behaviors that you setupped before. Mock contains information from when(), verify() and controls it , call reset - it's clean all information.

Spring MVC InternalResourceViewResolver

I'm a newer of Spring MVC , and I'm trying to config a simple controller like below ,but when i test it. I got
javax.servlet.ServletException: Circular view path [index]: would dispatch back to the current handler URL [/index] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
Here is my WebConfig.java Code:
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
Here is my IndexController.java Code:
#Controller
#RequestMapping("/index")
public class IndexController {
#RequestMapping(method = RequestMethod.GET)
public String index() {
return "index";
}
}
Here is my Test Fiel :
public class TestIndexController {
#Test
public void testIndexController() throws Exception {
IndexController indexController = new IndexController();
MockMvc mockMvc = standaloneSetup(indexController).build();
mockMvc.perform(get("/index")).andExpect(view().name("index"));
}
}
Every time when i changed the get("/index") to get("/index.jsp") ,i passed the test. but i just can't figure it out, please help me out
In your test setup, you create an instance of the Controller under test, but the context created for the test has no knowledge of the existence of your WebConfig and therefore, no instance of your ViewResolver.
Here is a quick fix :
public void testIndexController() throws Exception {
IndexController indexController = new IndexController();
MockMvc mockMvc = standaloneSetup(indexController)
setViewResolvers((new WebConfig()).viewResolver()).build();
mockMvc.perform(get("/index")).andExpect(view().name("index"));
}
If you don't want to access WebConfig in your test class, you can also create a new instance of ViewResolver and add it to your mockMvc setup.
Hope this helps.

Spring MockMvc test: Null pointer Exception

I'm following tutorials for integrating Spring docs into my project but i'm running into nullpointerexception when I run my test.
The errors go away when I take out all the document bits. So when I remove restDocumentation variable, the document bit from the setup method and the test then it passes.
Here is my test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class, classes = { ContextConfiguration.class })
#WebAppConfiguration
public class ApiDocs {
#Rule
public RestDocumentation restDocumentation = new RestDocumentation(
"${project.basedir}/target/generated-snippets");
private RestDocumentationResultHandler document;
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Autowired
Config Config;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).alwaysDo(this.document).build();
}
#Test
public void getConfig() throws Exception {
this.mockMvc.perform(get("/config").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("index"));
}
}
The error that I'm getting(I've slashed out my class package due to privacy):
java.lang.NullPointerException
at org.springframework.test.web.servlet.MockMvc.applyDefualtResultActions(MockMvc.java:195)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:163)
at //.//.//.//.//.ApiDocs.getConfig(ApiDocs.java:67)
at org.springframework.test.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at
org.springframework.restdocs.RestDocumentation$1.evaluate(RestDocumentation.java:59)
at org.springframework.test.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.springframework.test.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.springframework.test.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:75)
at org.springframework.test.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:75)
at org.springframework.test.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
Nothing's assigning a value to your this.document field so it's null. You're passing that into alwaysDo which then causes a NullPointerException.
You need to configure what you want to always happen. For example by adding this to the beginning of your setUp method:
this.document = document("{method-name}",
preprocessRequest(removeHeaders("Foo")),
preprocessResponse(prettyPrint()));
There's more information about this in the documentation.

Spring Rest Controller skip method argument validation in unit test

I have configured Spring to validate controller method arguments, by adding MethodValidationPostProcessor bean in application configuration, and adding #validated annotation on controller.
public Entity getEntity(#MyConstraint #RequestParam int limit)
MyConstraint validation is applied in application, but when running unit test, validation is not triggered.
Test class looks like:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class ControllerTest {
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller).setControllerAdvice(new ControllerExceptionHandler())
.build();
}
#Test
public void testCase() throws Exception
{
mockMvc.perform(get("locale?limit=-1")).andExpect(status().isBadRequest());
}
Any idea what is wrong with my test?
Thanks.
I think you need to add #WebAppConfiguration annotation to your Test Class.

Spring MVC test case

Am new to Spring MVC, i have written web servise using spring MVC and resteasy. My controller is working fine, now need to write testcase but i tried writtig but i never succed am also getting problem in autowiring.
#Controller
#Path("/searchapi")
public class SearchAPIController implements ISearchAPIController {
#Autowired
private ISearchAPIService srchapiservice;
#GET
#Path("/{domain}/{group}/search")
#Produces({"application/xml", "application/json"})
public Collections getSolrData(
#PathParam("domain") final String domain,
#PathParam("group") final String group,
#Context final UriInfo uriinfo) throws Exception {
System.out.println("LANDED IN get****************");
return srchapiservice.getData(domain, group, uriinfo);
}
}
can anyone give me sample code for Test case in spring mvc.
"Spring-MVC" Test case could seem like this using mock objects, for example we want to test my MyControllerToBeTest:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/spring.xml")
public class MyControllerTest {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MyControllerToBeTested controller;
private AnnotationMethodHandlerAdapter adapter;
#Autowired
private ApplicationContext applicationContext;
#Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
response.setOutputStreamAccessAllowed(true);
controller = new MyControllerToBeTested();
adapter = new AnnotationMethodHandlerAdapter();
}
#Test
public void findRelatedVideosTest() throws Exception {
request.setRequestURI("/mypath");
request.setMethod("GET");
request.addParameter("myParam", "myValue");
adapter.handle(request, response, controller);
System.out.println(response.getContentAsString());
}
}
but i don't have any experience with REST resource testing, in your case RestEasy.
If you want to test the full service inside the container you can have a look at the REST Assured framework for Java. It makes it very easy to test and validate HTTP/REST-based services.

Resources