Spring controller test fails with 404 when #PathVariable is a string - java

I am trying to test a Spring controller method - the method is at the end of the post and the test class below that. I've stripped it back a bit to try and narrow down the problem.
When I run the test as is it fails:
java.lang.AssertionError: Status
Expected :200
Actual :404
If I edit the mockMvc.perform as follows then the test passes, I don't even have to change the #PathVariables in the controller to be Longs:
mockMvc.perform(get(ApplicationEndPoints.GET_CCS_NAME_AND_ADDRESS_AJAX, 1L, 2L))
.andExpect(status().isOk());
The controller method itself works fine and returns JSON as expected. Can I just use these Long values and expect the test to be ok or how can I get it to work with Strings?
I should add that I'm a total testing noob. Thanks!
controller method:
#PreAuthorize("hasAuthority('auditor')")
#RequestMapping(value = ApplicationEndPoints.GET_APPLICANT_DATA, method = RequestMethod.GET)
#ResponseBody
public ApplicantData getNameAndAddress(#PathVariable("businessId") String businessId, #PathVariable("date") String date) {
//Date d = Date.valueOf(date);
ApplicantParams params = new ApplicantParams();
//params.setBusinessId(businessId);
//params.setApplicationReceivedDate(d);
params.setRoleId(ADDRESS_ROLE.HR.getRoleId());
return applicantService.getApplicantData(params);
}
test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ApplicationTestConfig.class})
#WebAppConfiguration
public class ClientDetailAjaxControllerTest {
#InjectMocks
private ClientDetailAjaxController clientDetailAjaxController;
private MockMvc mockMvc;
private ApplicantServiceInterface<Applicant> applicantService = Mockito.mock(ApplicantServiceImpl.class, Mockito.RETURNS_DEEP_STUBS);
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(clientDetailAjaxController).build();
}
#Test
public void getNameAndAddress() throws Exception {
Mockito.when(applicantService.getApplicantData(Mockito.any(ApplicantParams.class)))
.thenReturn(ApplicationTestData.getApplicantData());
mockMvc.perform(get(ApplicationEndPoints.GET_APPLICANT_DATA, Mockito.anyString(), Mockito.anyString()))
.andExpect(status().isOk());
Mockito.verify(applicantService, Mockito.times(1)).getApplicantData(Mockito.any(ApplicantParams.class));
Mockito.verifyNoMoreInteractions(applicantService);
}
}
EDIT: I have cleared up one or two things in response to comments...

404 tells you that mockMVC cannot match the controller method you're trying to call.
I'm noticing that in your controller, the path is ApplicationEndPoints.GET_APPLICANT_DATA, while in the test you've shown the path is ApplicationEndPoints.GET_CCS_NAME_AND_ADDRESS_AJAX.
Nevertheless - don't use Mockito.anyString() in place of parameter values you have to supply to your RequestBuilder in the mockMVC perform method.
Here's an idea how code should look like (derived from your example):
To test this controller method:
#RequestMapping(value = "/applicant/name-and-address/", method = RequestMethod.GET)
public ApplicantData getNameAndAddress(#PathVariable("businessId") String businessId, #PathVariable("date") String date) {
// some code
}
You need to do something like:
mockMvc.perform(get("/applicant/name-and-address/{businessId}/{date}", "myBusinessId", "myDateAsString")).andExpect(status().isOk());
The difference is that you have to pass actual string values, not Mockito.anyString().
Some other notes:
If your URL path variable has the same name as the parameter you're binding it to, you can write #PathVariable String id, instead of #PathVariable("id") String id.
you can use #GetMapping in place of #RequestMapping for GET method, #PostMapping for POST etc.

Related

MockMvc can't validate with param #DateValid

I'm using MockMvc to test my method that has a date parameter. The parameter is annotated with #DateValid but MockMvc doesn't trigger the validator and returns success when I input a wrong date.
StudentControler
public ResponseEntity<?> getStudents(#PathVariable("id") String studentId,
#RequestParam("birthDate") #DateValid String Date) {
//do something
}
Test
public class StudentController extends AbstractControllerTest {
#Test
public void testStudents_validRequest() throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("birthDate","aaaa");
MvcResult result = getMvc().perform(get("/student/{studentId}", "test")
.params(params)
.characterEncoding(StandardCharsets.UTF_8.name())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().json(mapToJson(studentResDto)))
.andReturn();
verify(studentService).getStudentId(anyString(), anyString());
log(log, result);
}
}
I do not understand why because when I test using Postman it works as expected. Postman does validate but MockMvc does not?
I using Spring-Boot 2.1.3
I can think of 2 problems here:
The validation annotation class is not loaded properly in your test. This seems to happen primarily when combining Spring validation with Hibernate validation (see also this question on how to fix that.
Your test is not setup properly. Could it be that you are not fully loading your controller in your mock test? Perhaps you are mocking the controller accidentally?

How to unit test a Controller method that has a RequestParam of an object?

I have a controller mapping
#RequestMapping(value = "/something", method = RequestMethod.GET)
public String get(#RequestParam("id") Person aPerson, Model aModel) {
aModel.addAttribute("person", aPerson);
return "index";
}
How do I go about testing this through MockMvc?
I can do something like this
mockMvc.perform(get("/something?id=1")).andExpect(status().is2xxSuccessful());
But this will not work since the RequestParam is an object, not a String. The conversion is done by Spring but I'm unit testing the method and do not want to start up the application context. How do I unit test something like this with MockMvc?
you may try something like
#Test
public void test() throws Exception {
Model model= new Model();
mockMvc.perform(MockMvcRequestBuilders.get("/something?id=1")
.content(asJsonString(model))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andExpect(status().is2xxSuccessful());
}

Is there a way to verify that a method on Spring Controller was called using Mockito

This is my Mockito Test:
#RunWith(MockitoJUnitRunner.class)
public class Controller_Test {
private MockMvc mockMvc;
#InjectMocks
private EmployeeController employeeController;
#Mock
private InputValidationService inputValidationService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(restController).build();
}
#Test
public void testGetEmployeeDetails() {
EmployeeController spy = Mockito.spy(employeeController);
MvcResult result = mockMvc.perform(get("/employee/details/9816")).andDo(print()).andExpect(status().isOk()).andReturn();
// Have some basic asserts here on the result that are working fine
}
}
Now my question is, Is there a way to assert that the method I expected to be called in my controller was actually invoked.
I know it was, but how do I assert it with unit test
For e.g.
This is my RequestMapping in the controller:
#RequestMapping(value = "/employee/details/{id}", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
#ResponseStatus( HttpStatus.OK)
#ResponseBody
public EmployeeDetails getEmployeeDetailsById(#PathVariable String employeeID) {
//Some business logic
}
Now I would like to make some assertion like:
Mockito.verify(spy, times(1)).getEmployeeDetailsById();
So basically I would like to assert that the method I expected to get called was the one that got called. I know this can be done on the Mock Service object that I have i.e. inputValidationService but would like something similar for the controller as well.
Please let me know if there are any additional details that you would like me to post.
Maybe late, but I found org.springframework.test.web.servlet.result.HandlerResultMatchers which can verify the proper controller and method are being called. For example:
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.handler;
mockMvc
.perform(get("/employee/details/9816"))
.andExpect(handler().handlerType(EmployeeController.class))
.andExpect(handler().methodName("getEmployeeDetailsById"));
You can use #MockBean.This will use Mockito beans for which you will be able to use standart Mockito functions like "verify".
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-testing-spring-boot-applications-mocking-beans

A spring integration test

In my integration test, I tried to use resttemplate to send a Get request to a dummy server created by MockMvcBuilders. However I got an error:
I/O error on GET request for "http://localhost:8080/test":Connection refused:
(In the function testAccess(), url is "http://localhost:8080/test"). My code is as below:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#IntegrationTest("server.port=8080")
public class MyTest {
private MockMvc mockMvc = null;
#Autowired
private WebApplicationContext context;
#Value("${server.port}")
private int port;
#Autowired
private MyController myController;
#Before
public void setUp(){
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.build();
}
#Test
public void testAccess() throws Exception{
RestTemplate restTemplate=new RestTemplate();
String url="http://localhost:8080/test";
try{
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
}
catch(ResourceAccessException ex){
System.out.println(ex.getMessage());
}
}
#Controller
public static class MyController {
#RequestMapping(value = "/test", method = RequestMethod.GET)
public #ResponseBody String access() {
return "OK";
}
}
}
The way I've done it is this:
First, you create a mock server from the actual RestTemplate you are using in your application
#Before
public void setup() throws Exception {
mockServer = MockRestServiceServer.createServer(myService.restTemplate);
}
Then you define how that request is going to work:
mockServer.expect(requestTo("http://localhost:8080/myrestapi"))
.andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{ success: true }", MediaType.APPLICATION_JSON));
And last you call the method in your application that will trigger a call to that url with that RestTemplate:
#Test
public void testThis() throws Exception {
myService.somethingThatCallsMyRestApi(parameters);
}
That will make your tests work as if there was a server up and running to process requests.
Using this in your example makes no sense, cause you would be testing that you build your test correctly and nothing else from the actual application.
The problem with this is that you cannot test dynamic responses. I mean, in my case the method I'm calling generates different data every time you call it and then sends it to the mockServer and then validates that the response matches in some very specific way. I haven't found a solution yet, but if the data you are going to send and receive is previously known, which would be in most cases, you'll have no problem using this.
Why are you defining a controller in your Test class and then trying to test it ? It doesn't feel logical to try to test something that is defined within the test it self.
Rather you would want to test a controller defined somewhere outside your tests, an actual controller that is used within your application.
Let's say MyController is defined as an actual controller then you could use the mockMvc object you created to test it.
mockMvc.perform(get('/test'))
.andExpect(status().isOk())

How to test spring's #RequestBody which recieves custom object using MockMvc

I am trying to test my controller method which takes input parameter as One of the objects in my application.I know how to test with String as input parameter,but not with customized object's.
Below is the code in my app
#RequestMapping(value = "/someUrl", method = RequestMethod.POST)
public ResponseEntity<?> save(#RequestBody Transaction transaction) {
I thought below test code is one of the ways to test.but it's failing
#Test
public void test() throws Exception {
Transaction Transaction = new Transaction();
Gson gson = new Gson();
String json = gson.toJson(transaction);
mockMvc.perform(post("/someUrl")
.contentType(MediaType.APPLICATION_JSON)
.content(json));
When I run the above test,I am getting error saying "Actually, there were zero interactions with this mock".
Could you please help me on how to pass Customized object to MockMvc post method.
mapper.writeValueAsString(json) is a better approach to do it

Categories