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

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.

Related

Spring mvc unit test controller inject dao object using mockito

When using mockito to unit test Spring mvc controller, how to inject dao layer object. It's always giving null pointer exception with #Spy annotation when making use of SpringJUnit4ClassRunner class.
Sample code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:**/evivecare-application-context-test.xml" })
#WithMockUser(username = "admin", roles={"ADMIN"})
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
public class ControllerTest {
private MockMvc mockMvc;
#Mock
private SessionFactory sessionFactory;
#Mock
private Session session;
#InjectMocks
private FilterController filterController = new FilterController();
#Spy
private FilterService filterService= new FilterServiceImpl();
#Autowired
private FilterDAO filterDAO;
#Mock
private OperatorService userService;
#Mock
private EviveSpeechFilterService eviveSpeechFilterService;
private TestContextManager testContextManager;
#Before
public void setup() throws Exception {
// Process mock annotations
MockitoAnnotations.initMocks(this);
// Setup Spring test in standalone mode
this.mockMvc = MockMvcBuilders.standaloneSetup(filterController).build();
testContextManager = new TestContextManager(getClass());
testContextManager.prepareTestInstance(this);
filterDAO= new FilterDAOImpl(sessionFactory);
Mockito.doReturn(session).when(sessionFactory).getCurrentSession();
}
#Test
public void testController200() throws Exception{
Mockito.when(filterService.renameList("123","sdfgh")).thenReturn(false);
Mockito.when(filterDAO.renameList("123","sdfgh")).thenReturn(false);
this.mockMvc.perform(org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post("/renameList")
.sessionAttr("filterService", filterService)
.sessionAttr("filterDAO", filterDAO)
.param("listId", "1234567")
.param("alternateName", "LIst Name"))
.andExpect(org.springframework.test.web.servlet.result.MockMvcResultMatchers.status().isOk());
}
}
In this test case, the filterService in turn calls filterDAO, which is always returning null pointer exception.
So, what can I do to resolve this issue?
FilterService is not a managed bean, you probably need to inject the dao in the constructor since it won't be autowired inside filterService.
Please refer to this question on SO for more info: Support for autowiring in a class not instantiated by spring (3)

Setting up MockMvc with #WebMvcTest in Spring Boot 1.4 MVC Testing

I have few working code to set up MockMVc in different ways with the new Spring Boot 1.4 #WebMvcTest. I understand the standaloneSetup approach. What I want to know is the difference between setting up MockMvc through WebApplicationContext and by autowiring MockMvc.
Code Snippet 1: MockMvc through WebApplicationContext Setup
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ProductController.class)
public class ProductControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private ProductService productServiceMock;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testShowProduct() throws Exception {
Product product1 = new Product();
/*Code to initialize product1*/
when(productServiceMock.getProductById(1)).thenReturn(product1);
MvcResult result = mockMvc.perform(get("/product/{id}/", 1))
.andExpect(status().isOk())
/*Other expectations*/
.andReturn();
}
}
As per WebMvcTest API documentation, By default, tests annotated with #WebMvcTest will also auto-configure Spring Security and MockMvc. So, I expected a 401 Unauthorized status code here, but the test passes with a 200 status code.
Next, I tried auto wiring MockMvc, but the test fails with 401 Unauthorized status code, unless I add #AutoConfigureMockMvc(secure=false) or update the #WebMvcTest annotation to disable security:
#WebMvcTest(controllers = IndexController.class, secure = false)
Following is the code that passes ONLY AFTER explicitly disabling security.
Code Snippet 2: MockMvc through Autowiring
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ProductController.class)
#AutoConfigureMockMvc(secure=false)
public class ProductControllerTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private ProductService productServiceMock;
#Test
public void testShowProduct() throws Exception {
Product product1 = new Product();
/*Code to initialize product1*/
when(productServiceMock.getProductById(1)).thenReturn(product1);
MvcResult result = mockMvc.perform(get("/product/{id}/", 1))
.andExpect(status().isOk())
/*Other expectations*/
.andReturn();
}
}
So my questions are:
Why didn't Code snippet 1 report a a 401 Unauthorized status code error while auto wiring MockMvc did. Also reiterating what the official doc says By default, tests annotated with #WebMvcTest will also auto-configure Spring Security and MockMvc. But, in this case it appears #WebMvcTest has nothing to do with auto-configuring Spring Security (Because Code Snippet 1 passes without any 401 error). It finally boils down to how I set up the MockMvc. Am I correct here?
What are the differences/objectives between/of both the approaches?
How does disabling security via #AutoConfigureMockMvc(secure=false) differs from doing through #WebMvcTest(controllers = IndexController.class, secure = false). Which one is the preferred approached or when (or where) to use them?
I also come across similar problem. #WebMvcTest auto configures Spring Security with basic auth but I have a WebSecurityConfig class that extends WebSecurityConfigurerAdapter. In this class I disabled basic auth and configured token base security. That means WebSecurityConfig class is not used to configure Spring Security.
To resolve the problem, I added #ContextConfiguration to my unit test class and added mocks of dependencies of WebSecurityConfig class.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = CategoryRestService.class)
#ContextConfiguration(classes = {MjApplication.class, WebSecurityConfig.class})
public class CategoryRestServiceTest {
#MockBean
private CategoryRepository repository;
#MockBean
CurrentUserDetailsService currentUserDetailsService;
#MockBean
TokenAuthProvider tokenAuthProvider;
#Autowired
MockMvc mockMvc;
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));
#Test
public void getCategories() throws Exception {
Category category1 = new Category();
category1.setName("Test Category 1");
category1.setId(1L);
Category category2 = new Category();
category2.setName("Test Category 2");
category2.setId(2L);
List<Category> categoryList = new ArrayList<Category>();
categoryList.add(category1);
categoryList.add(category2);
given(this.repository.findAll())
.willReturn(categoryList);
mockMvc.perform(get("/public/rest/category"))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$[0].id", is(1)))
.andExpect(jsonPath("$[0].name", is("Test Category 1")))
.andExpect(jsonPath("$[1].id", is(2)))
.andExpect(jsonPath("$[1].name", is("Test Category 2")));
}
}
According to this issue in github
https://github.com/spring-projects/spring-boot/issues/5476
#WebMvcTest auto configures by default, a basic auth when spring-security-test is in the class path
Answering your questions:
In the code snippet 1, you not injected the MockMvc in your test class, you should add .apply(springSecurity()) in the builder on the setup method, so spring would use the basic configuration (not your custom security configuration if you have one)
both approaches does basically the same thing, the difference is that the second comes with the basic auth already in the MockMvc, that is why you have to use the secure=false
From the documentation:
By default, tests annotated with #WebMvcTest will also auto-configure
Spring Security and MockMvc (include support for HtmlUnit WebClient
and Selenium WebDriver). For more fine-grained control of MockMVC the
#AutoConfigureMockMvc annotation can be used.
I'm not sure this is directly related, but there is an outstanding bug where, if using spring boot and #WebMvcTest, your custom #EnableWebSecurity config class will be ignored. A couple workarounds are mentioned in the bug report. I'm using:
#WebMvcTest(includeFilters = #Filter(classes = EnableWebSecurity.class))
I don't know if this is correct way or not but I could disable configurations class by using like below
#WebMvcTest(ProductController.class)
#ContextConfiguration(classes = ProductController.class)
public class ProductControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private ProductService productServiceMock;

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.

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

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