Spring webAppContextSetup does not recognize controllers mappings - spring-mvc

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")

Related

Mockito test for service call on Legacy code

I am creating junit test case for legacy spring controller code which calls service (creates new instance of service call instead of autowire/spring bean). I want the service class call to be mocked. Please let know if it's possible. Changing the legacy code is not an option for me.
public class WebController {
#PostMapping("/api/op1")
public #ResponseBody String validate(ModelMap model,HttpSession session,HttpServletRequest request){
---
--
Service service = new Service();
service.invoke(param1,param2);
----
---
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public class WebControllerTest{
private MockMvc mockMvc;
private WebController classUnderTest;
#Test
public void validat() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
//Service service = spy(Service.class);
//doReturn(new ServiceCallResponse()).when(service).validate( any(String.class),any(String.class));
Service service = Mockito.mock( Service.class, CALLS_REAL_METHODS );
doReturn(new ServiceCallResponse()).when(service).validate(any(String.class),any(String.class));
MvcResult result = mockMvc.perform(
post("/validate").params(params).session(new MockHttpSession()))
.andExpect(status().isOk())
.andReturn();
}
}
Spy and Mocktio calls real method options are not working. Please let know how to mock it.

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.

Test Spring Mvc controller and inject static class

The following code is the standard method to write a JUnit test for a Mvc controller.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationTestCassandra.class)
#WebAppConfiguration
public class TestControllerTests {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testupTimeStart() throws Exception {
this.mockMvc.perform(get("/uptime"))
.andExpect(status().isOk());
}
}
This works fine, but I would like to replace an autowired class with a special class for testing. The class CassandraSimpleConnection is injected via #Autowired in my controller.
I have tried several approaches, but no luck.
The following code fails because of an Mvc 404 error, because I guess my application with the REST interface is not running at all.
#RunWith(SpringJUnit4ClassRunner.class)
//ApplicationTestCassandra is SpringBoot application startpoint class with #SpringBootApplication annotation
//#ContextConfiguration(classes = ApplicationTestCassandra.class, loader = AnnotationConfigContextLoader.class)
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class)//, classes = {ApplicationTestCassandra.class})
#WebAppConfiguration
public class TestControllerTests {
#Service
#EnableWebMvc
#ComponentScan(basePackages={"blabla.functionalTests"})
static class CassandraSimpleConnection {
public Metadata testConnection(TestConfiguration configuration) {
Metadata metadata = null;
// return metadata;
throw new RuntimeException("Could not connect to any server");
}
}
If I use
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class, classes = {ApplicationTestCassandra.class})
CassandraSimpleConnection is not replaced with my static class.
Could somebody help me please? The documentation about the annotations is quite confusing.
Read the comments and here is the solution:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { MyApplication.class })
public class MyTests {
#MockBean
private MyBeanClass myTestBean;
#Before
public void setup() {
...
when(myTestBean.doSomething()).thenReturn(someResult);
}
#Test
public void test() {
// MyBeanClass bean is replaced with myTestBean in the ApplicationContext here
}
}

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.

JUnit test for Spring controller. Expected "200", but was "404"

I am trying to test my Spring MVC controller with JUnit and I get this:
java.lang.AssertionError: Status expected:<200> but was:<404>
I guess my JUnit setup for controller tests isn't working like it should, but I really can't point out where the mistake is. I've read so many tutorials about this that it's getting frustrating.
This is simplyfied version of my LoginController class, but it will be enough
#Controller
public class LoginController {
#Autowired
private DAO dao;
#RequestMapping(value = "username", method = RequestMethod.GET)
public String GetUsername(Model model){
model.addAttribute("username", dao.getUsername());
return "login"; //login.jsp
}
And this is JUnit class which is testing that controller:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("file:src/main/webapp/WEB-INF/Spring-Context.xml")
public class LoginControllerTest {
#Mock
private DAO dao;
#InjectMocks
LoginController controller;
private MockMvc mockMvc;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void test() throws Exception{
mockMvc.perform(get("username")).andExpect(status().isOk());
}
}
And I get "404". Same thing if I test example "forwardedUrl("/PATH/login.jsp")" the result is null.
My Spring-Context.xml has
mvc:annotation-driven />
My console error is:
org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping found for HTTP request with URI [username] in DispatcherServlet with name ''
I don't really get that, because after all my application is working like it should so there is no problems with my mappings.

Resources