Spring Rest Controller skip method argument validation in unit test - spring-mvc

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.

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 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.

How to write a mockito test case for ResourceAssembler with in Spring Hateos?

I am trying to write a unit test for the below Assembler but i keep getting Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler?. I wanted to know how i can mock out the resource creation?
#Component
public class LoginResourceAssembler extends ResourceAssemblerSupport<User, ResourceSupport> {
public LoginResourceAssembler() {
super(User.class, ResourceSupport.class);
}
#Override
public ResourceSupport toResource(User user) {
ResourceSupport resource = new ResourceSupport();
final String id = user.getId();
resource.add(linkTo(MyAccountsController.class).slash(id).slash("accounts").withRel("accounts"));
return resource;
}
}
Instead of changing from a plain unit test to a IMO integration test (given dependency of the spring framework) you could do something like:
#RunWith(MockitoJUnitRunner.class)
public class LoginResourceAssemblerTest {
#InjectMocks
private LoginResourceAssembler loginResourceAssembler;
#Before
public void setup() {
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
}
#Test
public void testToResource() {
//...
}
}
I was seeing the error Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler because my test class was annotated with #RunWith(MockitoJUnitRunner.class) and this was not injecting the controller.
To fix this error, i annotated my test case with
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
A working test case in my case
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class LoginResourceAssemblerTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#InjectMocks
private LoginResourceAssembler loginResourceAssembler;
#Before
public void setUp() {
initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
#Test
public void testToResource() {
User user = new User();
user.setId("1234");
ResourceSupport resource = loginResourceAssembler.toResource(user);
assertEquals(1,resource.getLinks().size());
assertEquals("accounts",resource.getLinks().get(0).getRel());
assertTrue(resource.getLinks().get(0).getHref().contains("accounts"));
}
}

Spring webAppContextSetup does not recognize controllers mappings

My unit test fails when testing a controller. I am getting a 404 when expecting a 200 status code. I suppose the webAppContextSetup does not recognize controllers.
Here is my code:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#ContextConfiguration(classes = MyConfig.class)
public class MyControllerTest
{
#Before
public void setup()
{
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
mockMvc.perform(get("/locales?limit=1")).andExpect(status().isOk());
}
The message is "No mapping found for HTTP request with URI [/locales] in DispatcherServlet with name ''"
This works in the real application.
Thanks.
Try adding component scan for the package which contains controllers:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
#ContextConfiguration(classes = MyConfig.class)
#ComponentScan("x.y.z")

Isolated Controller Test can't instantiate Pageable

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();

Resources