Spring boot Integration test RestController validation - spring-boot

I create one #RestController, so I need to test my validation, so I create one model with and dont set any value, so my app will throw a lot of errors, so now I need to test this validation.
I create this method to validate:
#Test
public void selarAberturaMatriculaFail() throws Exception {
AberturaMatricula aberturaMatricula = new AberturaMatricula();
MockHttpServletRequestBuilder requ = post("/ri/selo/abertura/").contentType(contentType).content(this.jsonWithClass(aberturaMatricula));
mockMvc.perform(requ)
.andExpect(model().attributeHasErrors("cns"));
}
but I got this error:
No ModelAndView found java.lang.AssertionError: No ModelAndView found
this is my Rest method:
#RestController
#RequestMapping("/ri")
public class RIController {
#RequestMapping(value = "/selo/abertura/", method = RequestMethod.POST)
public AberturaMatricula selarAbertura(#RequestBody #Valid AberturaMatricula aberturaMatricula){
...
}
}
In my model I have cns property and more..

You are not going to want to use a model test with a REST controller as REST simply returns the object and not a model and view. See https://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#spring-mvc-test-framework as an example. This tutorial http://spring.io/guides/tutorials/bookmarks/ also shows how to build some tests for REST services.

Related

Spring 5 with JUnit 5 + Mockito - Controller method returns null

I try to test a method named as loadData defined in MainController which returns result as a string. Despite that this method actually returns data when the web app runs on servlet container (or when I debug the code), no data returns when I invoke it from a test class based on JUnit 5 with Mockito.
Here is my configuration:
#ContextConfiguration(classes = {WebAppInitializer.class, AppConfig.class, WebConfig.class})
#Transactional
#WebAppConfiguration
public class TestMainController {
#InjectMocks
private MainController mainController;
private MockMvc mockMvc;
#BeforeEach
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(this.mainController).build();
}
#Test
public void testLoadData() throws Exception {
MvcResult mvcResult = mockMvc
.perform(MockMvcRequestBuilders.get("/loadData.ajax"))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
Assertions.assertNotNull(mvcResult.getResponse().getContentAsString(), "response should not be null");
}
}
The test fails due to java.lang.NullPointerException as the this.mainController is null.
Environment Details:
Spring version: 5.0.3
JUnit version: 5.0.3
mockito version: 1.9.5
hamcrest version: 1.3
json-path-assert version: 2.2.0
Edit: Here is the loadData method of MainController:
#RequestMapping(value = "/loadData.ajax", method = RequestMethod.GET)
public String loadData(HttpServletRequest request, HttpServletResponse response) {
List list = mainService.loadData(); // starts a transaction and invokes the loadData method of mainDAO repository which basically loads data from the database
return JSONArray.fromObject(list).toString();
}
You can call controller method directly, just like we do for service method, but this is not recommended. Using MockMvc, you check for header and request param mapping are proper. Plus, you also check for end point mapping is correct. Plus the Request METHOD is also correct. All these you cannot test if you test your code by directly calling the controller method.
One thing you can try is, instead of creating new object inside the standalone context, use the Mock. i.e
mockMvc = MockMvcBuilders.standaloneSetup(this. mainController).build();
And while calling, do this
MvcResult mvcResult = mockMvc
.perform(MockMvcRequestBuilders.get("/loadData.ajax"))
.andExpect(MockMvcResultMatchers.status().isOk()).andReturn();
Assert , what you would like to
Assert.assertEquals("response does not match", mvcResult.getResponse().getContentAsString(),
"some expected response");
You are getting null or 400 or 404 http status ?
If you are getting 400, then please check the header and req. param if any are proper. If you are getting 404 then please check the URL path. /loadData.ajax , assuming this is your request mapping path in controller method.

Unit Test POST with #WebMvcTest - #MockBean Service returns null

I'm trying to unit test a controller to save a Brand entity. In this test, I'm creating a Brand that I expect to get back and then I'm posting JSON to the controller. Originally, I was relying on pass-by-reference so my controller method was basically doing:
#Override
public ResponseEntity<MappingJacksonValue> save(#Valid #RequestBody Brand brand, BindingResult bindingResult) {
validate(brand, null, bindingResult);
if (bindingResult.hasErrors()) {
throw new InvalidRequestException("Invalid Brand", bindingResult);
}
this.brandService.save(brand); // pass by reference
MappingJacksonValue mappingJacksonValue = jsonView(JSON_VIEWS.SUMMARY.value, brand);
return new ResponseEntity<>(mappingJacksonValue, HttpStatus.CREATED);
}
Note that I wasn't actually using the returned Brand from the service. When I tested this way, my test was failing because the controller was returning the JSON I passed in, and since the service is mocked, the controller didn't return the brand I was expecting, which was to have an ID. So I changed the controller to do this:
brand = this.brandService.save(brand);
However, when I debug, the brand returned from the mocked service is null. Below is my test.
#RunWith(SpringRunner.class)
#WebMvcTest(BrandController.class)
public class BrandSimpleControllerTest {
#Autowire
private MockMvc mockMvc;
#MockBean
private BrandService brandService;
#Test
public void testSave() throws Exception {
Brand brand = new Brand();
brand.setId(1L);
brand.setName("Test Brand");
when(this.brandService.save(brand)).thenReturn(brand);
this.mockMvc.perform(this.post("/api/brands")
.content("{\"name\": \"Test Brand\"}"))
.andExpect(jsonPath("$.id", is(1)))
.andExpect(jsonPath("$.name", is("Test Brand")));
}
}
Any suggestions?
Alright, problem solved. The issue was that the object you mock in the service call has to be identical to the object passed into the controller so when the mock looks at what is expected, it says "oh, you gave me this and so you want that". Here's the modified code that makes it work:
Brand brand = new Brand();
brand.setId(1L);
brand.setName("Test Brand");
brand.setDateCreated(new LocalDateTime());
brand.setLastUpdated(new LocalDateTime());
// since all I'm passing into the controller is a brand name...
when(this.brandService.save(new Brand("Test Brand"))).thenReturn(brand);

Junit passing multiple parameters to rest service

I have a rest controller like bellow :
#RequestMapping(value = "/create", method = RequestMethod.POST)
public
#ResponseBody
GlobalResponse createDeal(#RequestBody Deal deal,#RequestBody Owner owner) {
// code here
}
I use Junit and Mockito for my test :
#Test
public void createDeal() throws Exception{
this.mockMvc.perform(post("/v1/Deal/create").content("\"deal\":{\"dealNumber\":\"DA001\"},\"owner\":{\"id\":1}").contentType(MediaType.APPLICATION_JSON)).andDo(print());
}
I cant past multiple parameters to the controller service , how can I avoid this ?
You won't be able to pass multiple arguments annotated with #RequestBody annotation. The argument annotated with this annotation holds the whole request body and it can't be split into multiple.
What you can do is to have a wrapper to hold your Deal and Owner objects and you can pass that wrapper as a single request body argument.
For e.g.:
public class Wrapper {
private Deal deal;
private Owner owner;
//Getters and setters
}
And your controller's method:
#RequestMapping(value = "/create", method = RequestMethod.POST)
public
#ResponseBody
GlobalResponse createDeal(#RequestBody Wrapper wrapper) {
// code here
}
Hope this makes sense.

Object reference changing during Mockito Test

I have just started working with Mockito and am having a problem with one of the tests failing, yet the actual code works correctly in a live environment. The controller being tested is:
#Controller
#RequestMapping("/notes")
public class NotesController {
private NoteRepository noteRepository;
private MyUserRepository userRepository;
#RequestMapping(value = "/add", method = POST)
public String postNote(#Valid Note note, BindingResult errors, Principal principal){
String username = principal.getName();
MyUser user = userRepository.findUserByUsername(username);
note.setMyUser(user);
note.setTime(new Date());
noteRepository.save(note);
return "redirect:/notes"; }
}
The test is here:
#Test
public void testShouldAddValidNote() throws Exception {
MyUser testing = new MyUser();
Note note = new Note();
NoteRepository noteRepository = mock(NoteRepository.class);
when(noteRepository.save(note)).thenReturn(note);
MyUserRepository userRepository = mock(MyUserRepository.class);
when(userRepository.findUserByUsername("testing")).thenReturn(testing);
Principal mockPrincipal = mock(Principal.class);
when(mockPrincipal.getName()).thenReturn("testing");
NoteController controller = new NoteController(noteRepository);
controller.setMyUserRepository(userRepository);
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(post("/notes/add")
.requestAttr("note", note)
.principal(mockPrincipal))
.andExpect(view().name("redirect:/notes"));
verify(noteRepository,times(1)).save(note);
verify(note,times(1)).setMyUser(testing);
verify(note,times(1)).setTime((Date)anyObject());
verify(userRepository,times(1)).findUserByUsername("testing");
verify(mockPrincipal,times(1)).getName();
}
The first verify test fails, with the message:
Argument(s) are different! Wanted:
noteRepository.save(projectName.Note#5ae9);
Actual invocation has different arguments:
noteRepository.save(projectName.Note#c079ae45
Clearly the Note object passed in to the method has changed, but I thought that using .requestAttr("note", note) would pass in the reference and the same object should therefore be in the method (and later returned). Like I said, it works perfectly in the live web Container, so what am I getting wrong with the Mockito test please?
This is just a wild guess could the issue this code faces comes from MockMvc / MockMvcRequestBuilders where the Note is somehow serialized / deserialized between the request configuration and the actual request ?
Note that note is a real object so calling verify(note).... won't work.
Anyway I suggest the use of the combination of a mockito captor and AssertJ in this case :
// if field instantiation if using the mockito runner / rule or MockitoAnnotations.initMocks(this)
#Captor ArgumentCaptor<Note> noteCaptor;
// if created in the test method
ArgumentCaptor<Note> noteCaptor = ArgumentCaptor.forClass(Note.class);
// ...
verify(noteRepository,times(1)).save(noteCaptor.capture());
assertThat(noteCaptor.getValue().geMyUser()).isEqualTo(testing);
assertThat(noteCaptor.getValue().geTime()).isCloseTo(someDate);
Note I'm on a phone

Spring MVC to Spring REST tutorials misunderstanding

I have developed a Spring MVC - Hibernate application as told here.
Now I am trying to modify this code to create a REST application as told here.
I have added Jackson library to the classpath and added #XmlRootElement.
#XmlRootElement(name = "persons")
public class Person implements Serializable {
But if I do a application/json request then I still get the html code back.
What I am doing wrong / forgot to do?
My controller:
#RequestMapping(value = "/persons", method = RequestMethod.GET)
#ResponseBody
public String getPersons(Model model) {
logger.info("Received request to show all persons");
// Retrieve all persons by delegating the call to PersonService
List<Person> persons = personService.getAll();
model.addAttribute("persons", persons);
return "personspage";
}
Changed the Controller, but get an error:
t
ype Status report
message /Buddies/WEB-INF/jsp/main/persons/1.jsp
description The requested resource (/Buddies/WEB-INF/jsp/main/persons/1.jsp) is not available.
Your controller should look like this:
#RequestMapping(value = "/persons/{id}", method = RequestMethod.GET)
#ResponseBody
public Person getPerson(#PathVariable int id) {
Person person = personService.getPersonById(id);
return person;
}
If you want to return a list of Person objects, you need an extra wrapper object, see: Using JAXB to unmarshal/marshal a List<String>.
You are probably missing AnnotationMethodHandlerAdapter and messageConverter in your spring configuration.

Resources