How to check returntype in Spring Unittest using MockMVC? - spring

The Spring method I wanna test
#RequestMapping(value="/files", method=RequestMethod.GET)
#ResponseBody
public List<FileListRequest> get() {
return getMainController().getAllFiles();
}
I want to be assured all calls to /files are responded with an List[FileListRequest]. How?
This is the method in which the test is supposed to be.
#Test
public void testGetAll() throws Exception {
this.mockMvc.perform(get("/files").accept("application/json"))
.andExpect(status().isOk())
.andExpect(content().contentType(SOMETHING);
}
Can I simply replace the SOMETHING or am I totally wrong?
Can I run assert methods on the object returned by perform()?

Edit:
MvcResult result = this.mockMvc.perform(get("/files").accept("application/json"))
.andExpect(status().isOk())
.andReturn();
String content = result.getResponse().getContentAsString();
// Convert json String to Respective object by using Gson or Jackson
ObjectMapper mapper = new ObjectMapper();
TypeFactory typeFactory=objectMapper.getTypeFactory();
List<SomeClass> someClassList =mapper.readValue(content , typeFactory.constructCollectionType(List.class, SomeClass.class));
//Assert here with your list
You could use Json Path to check if specific data exist in your response
a code snipper from by old project
mockMvc.perform(get("/rest/blogs")) .contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.blogs[*].title",
hasItems(endsWith("Title A"), endsWith("Title B"))))
.andExpect(status().isOk());

You cannot use contentType to check the class of instances. The Content-Type is to determine the format of text sent/returned in a HTTP(S) request/response, and has nothing to do with programmatic type-check. It only regulates that the request/response is in json/text-plain/xml, etc.
To check the type of the objects returned in the response, let's assume that the response is in format JSON(built-in Jackson in Spring boot will do the (un)marshalling), and we just use org.hamcrest.Matchers.instanceOf(Class<?> type) to check the class of first item in the list, with jsonPath.
A working snippet:
import static org.hamcrest.Matchers.instanceOf;
...
#Test
public void testBinInfoControllerInsertBIN() throws Exception {
when(this.repository.save(mockBinInfo)).thenReturn(mockBinInfo);
this.mockMvc.perform(post("/insert")
.content("{\"id\":\"42\", \"bin\":\"touhou\", \"json_full\":\"{is_json:true}\", \"createAt\":\"18/08/2018\"}")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
)
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(status().isCreated())
.andExpect(jsonPath("$[0]", instanceOf(BinInfo.class)))
.andExpect(jsonPath("$[0].bin", is("touhou")));
}
If you want to check every item in the list... maybe it is redundant? I haven't seen code examining each and every item in the list because you have to iterate. There is way, of course.

Related

MockMvc Test does not get to the endpoint for a Multipart file in a RestController

I am calling a service in an orders controller which receives a multipart file and processes it and saving it into a database. I am trying to create a Spring Rest Doc for it but it is not even hitting the endpoint. I am creating a list of orders which is what the service expects. It receives the order as a stream as shown and converts into a stream of orders before saving it into a database. I have shown the main part of the controller and my code for generating the rest docs. When I run the code I get the following exception, it never even hits the endpoint when I set a breakpoint. I also used fileupload() but that did not work either.
Exception is:
Content type = application/json
Body = {"path":"/orders/order_reception","exceptionName":
"MissingServletRequestPartException","message":"Required request part 'uploadFile' is not
present",
"rootExceptionName":"MissingServletRequestPartException",
"rootMessage":"MissingServletRequestPartException: Required request part 'uploadFile' is not present"}
#RestController
#RequestMapping(value = "/orders")
#Validated
class OrderController{
#PostMapping(path = "/order_reception")
public ResponseEntity receiveData(#RequestPart MultipartFile uploadFile,
HttpServletRequest request,
HttpServletResponse response) {
if (!uploadFile.isEmpty()) {
try {
Reader reader = new InputStreamReader(request.getInputStream()));
... save file
return new ResponseEntity<>(HttpStatus.HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
#Test
public void sendData() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Order order = repository.getOrder("1233333");
List<Order> orderList = new ArrayList<>():
resourceList.add(order);
MockMultipartFile orderFile = new MockMultipartFile("order-data", "order.json", "application/json",
mapper.writeValueAsString(orderList).getBytes(Charset.defaultCharset()));
mockMvc.perform(multipart("/orders/order_reception")
.file(orderFile))
.andExpect(status().isCreated())
.andDo(document("send-order",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())));
}
Thank you Marten Deinum, your suggestion that the file name was wrong fixed it.
I simply changed name in the MockMultipartFile( "uploadsFile", ...)

How to set at Spring Boot all Errors to json?

Is there at spring boot a configuration possible, which returns all errors in a json format?
For example 404, or 401. Need to replace this 404 page with just json.
Many thanks
This is since by default, springboot produces the error as html.
To get the Json output, add produces argument as follows so that the content returned will be for sure in json format.
#RequestMapping(....., produces = "application/json")
You can custom your error controller to handle it:
#RestController
public class CustomErrorController extends BasicErrorController {
private final Logger logger = LoggerFactory.getLogger(CustomErrorController.class);
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes, new ErrorProperties());
}
// let all MediaType return json data
#RequestMapping(consumes = MediaType.ALL_VALUE)
public ResponseEntity<Map<String, Object>> allError(HttpServletRequest request, HttpServletResponse response) {
return super.error(request);
}
}
You mean using #ControllerAdvice with the content header set to application/json

spring mockMVC testing method GET

i created post method in mockMVC (in spring boot project)
This is my method testing
This is my method testing
#Test
public void createAccount() throws Exception {
AccountDTO accountDTO = new AccountDTO("SAVINGS", "SAVINGS");
when(addaccountService.findByName("SAVING")).thenReturn(Optional.empty());
when(addaccountService.createAccount(any())).thenReturn(createdAccountDTO);
CreatedAccountDTO createdAccountDTO = new CreatedAccountDTO("a#wp.pl", "SAVINGS", "1234rds", uuid);
mockMvc.perform(
post("/account").contentType(MediaType.APPLICATION_JSON)
.content(asJsonString(AccountNewDTO)))
.andExpect(status().isCreated())
.andExpect(header().string("location", containsString("/account/"+uuid.toString())));
System.out.println("aaa");
}
I want to write GET method.
how to write a get method in mock mvc? how to verify whether what I threw was returned?
You can try the below for Mockmvc perform get and post methods
For get method
#Autowired
private MuffinRepository muffinRepository;
#Test
public void testgetMethod throws Exception(){
Muffin muffin = new Muffin("Butterscotch");
muffin.setId(1L);
BddMockito.given(muffinRepository.findOne(1L)).
willReturn(muffin);
mockMvc.perform(MockMvcRequestBuilders.
get("/muffins/1")).
andExpect(MockMvcResutMatchers.status().isOk()).
andExpect(MockMvcResutMatchers.content().string("{\"id\":1, "flavor":"Butterscotch"}"));
}
//Test to do post operation
#Test
public void testgetMethod throws Exception(){
Muffin muffin = new Muffin("Butterscotch");
muffin.setId(1L);
BddMockito.given(muffinRepository.findOne(1L)).
willReturn(muffin);
mockMvc.perform(MockMvcRequestBuilders.
post("/muffins")
.content(convertObjectToJsonString(muffin))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResutMatchers.status().isCreated())
.andExpect(MockMvcResutMatchers.content().json(convertObjectToJsonString(muffin)));
}
If the response is empty then make sure to override equals() and hashCode() method on the Entity your repository is working with
//Converts Object to Json String
private String convertObjectToJsonString(Muffin muffin) throws JsonProcessingException{
ObjectWriter writer = new ObjectWriter().writer().withDefaultPrettyPrinter();
return writer.writeValueAsString(muffin);
}
You can use the static get method of the class MockMvcRequestBuilders, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/web/servlet/request/MockMvcRequestBuilders.html#get-java.lang.String-java.lang.Object...-
Example:
mockMvc.perform(get("/account")).andExpect(...);
If you throw an exception within your controller method it will typically trigger execution of an exception handler which transforms the exception into a HTTP error response. By default, you could check if the status of the response was 500. If you have implemented your own exception handler you may want to check the response body as well to verify if it contains the expected error data.

How test Post request with custom object in content type application/x-www-form-urlencoded?

I have controller:
#PostMapping(value = "/value/", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String updateSettings(final Dto dto) {
System.out.println(">>> " + dto);
return "template";
}
Controller works if I send request across chrome window. But when I write test for this method I get problem. Not converted object, value not inserted.
Test:
#Test
#WithMockUser(username = FAKE_VALID_USER, password = FAKE_VALID_PASSWORD)
public void test_B_CreateDtoWithValidForm() throws Exception {
final Dto dto = new Dto();
dto.setId("value");
dto.setEnabled("true");
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.content(dto.toString()))
.andDo(print());
}
Output is >>> Dto{id=null, enabled=false}
How test Post request with custom object in content type application/x-www-form-urlencoded?
In this case you don't need to use content, but instead you need to use param in this way:
this.mockMvc.perform(post(URL_SET_PROVIDER_SETTINGS)
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "value")
.param("enabled", "true"))
.andDo(print());

MocMVC giving HttpMessageNotReadableException

I'm still learning my way around testing and I'm trying to get a MockMvc test to work for me. It's a simple REST controller that at this point is only doing some authentication using information from json in the post. I've actually implemented the code, so I know it's working because I get back both the correct response with the correct input and the error messages I've put together, both in a json format. My problem is that the test keeps failing with a HttpMessageNotReadableException, even though the actual code works, so I'm assuming I don't have my test set up right. Any help you guys can give would be great.
Here's my controller
#Controller
public class RequestPaymentController {
protected final Log logger = LogFactory.getLog(getClass());
private PaymentService paymentService;
private LoginService loginService;
#Autowired
public void setPaymentService(PaymentService paymentService){
this.paymentService = paymentService;
}
#Autowired
public void setLoginService(LoginService loginService){
this.loginService = loginService;
}
#RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
#ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(#RequestBody PaymentRequest paymentRequest, HttpServletRequest request, HttpServletResponse response, BindingResult result) throws Exception{
ResponseEntity<PaymentResult> responseEntity = null;
new LoginValidator().validate(paymentRequest, result);
boolean valid = loginService.isLoginValid(paymentRequest, result);
if (valid){
responseEntity = setValidResponse(paymentRequest);
}else {
throw new TumsException("exception message");
}
return responseEntity;
}
private ResponseEntity<PaymentResult> setValidResponse(PaymentRequest paymentRequest){
PaymentResult paymentResult = paymentService.getResults(paymentRequest);
return new ResponseEntity<PaymentResult>(paymentResult, HttpStatus.OK);
}
}
And here's my test code:
public class RequestPaymentControllerTest {
PaymentService mockPaymentService;
RequestPaymentController requestPaymentController;
HttpServletRequest mockHttpServletRequest;
HttpServletResponse mockHttpServletResponse;
PaymentRequest mockPaymentRequest;
BindingResult mockBindingResult;
LoginService mockLoginService;
PaymentResult mockPaymentResult;
MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockPaymentService = createMock(PaymentService.class);
mockHttpServletRequest = createMock(HttpServletRequest.class);
mockHttpServletResponse = createMock(HttpServletResponse.class);
mockPaymentRequest = createMock(PaymentRequest.class);
requestPaymentController = new RequestPaymentController();
mockBindingResult = createMock(BindingResult.class);
mockLoginService = createMock(LoginService.class);
requestPaymentController.setPaymentService(mockPaymentService);
mockPaymentResult = createMock(PaymentResult.class);
mockMvc = MockMvcBuilders.standaloneSetup(new RequestPaymentController()).build();
}
#After
public void tearDown() throws Exception {
mockPaymentService = null;
mockHttpServletRequest = null;
mockHttpServletResponse = null;
mockPaymentRequest = null;
requestPaymentController = null;
mockBindingResult = null;
mockLoginService = null;
mockPaymentResult = null;
mockMvc = null;
}
#Test
public void testHandleRequestPayment() throws Exception{
initializeStateForHandleRequestPayment();
createExpectationsForHandleRequestPayment();
replayAndVerifyExpectationsForHandleRequestPayment();
}
private void initializeStateForHandleRequestPayment(){
}
private void createExpectationsForHandleRequestPayment(){
mockPaymentRequest.getServiceUsername();
expectLastCall().andReturn("testuser");
mockPaymentRequest.getServicePassword();
expectLastCall().andReturn("password1!");
mockLoginService.isLoginValid(mockPaymentRequest,mockBindingResult);
expectLastCall().andReturn(true);
mockPaymentService.getResults(mockPaymentRequest);
expectLastCall().andReturn(mockPaymentResult);
}
private void replayAndVerifyExpectationsForHandleRequestPayment() throws Exception{
replay(mockPaymentService, mockBindingResult, mockHttpServletRequest, mockHttpServletResponse, mockPaymentRequest, mockLoginService);
requestPaymentController.setLoginService(mockLoginService);
requestPaymentController.handleRequestPayment(mockPaymentRequest, mockHttpServletRequest, mockHttpServletResponse, mockBindingResult);
mockMvc.perform(post("/requestpayment")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest());
verify(mockPaymentService, mockBindingResult, mockHttpServletRequest, mockHttpServletResponse, mockPaymentRequest, mockLoginService);
}
}
The results of the andDo(print()) are:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /requestpayment
Parameters = {}
Headers = {Content-Type=[application/json], Accept=[application/json]}
Handler:
Type = portal.echecks.controller.RequestPaymentController
Method = public org.springframework.http.ResponseEntity<portal.echecks.model.PaymentResult> portal.echecks.controller.RequestPaymentController.handleRequestPayment(portal.echecks.model.PaymentRequest,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,org.springframework.validation.BindingResult) throws java.lang.Exception
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 400
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Process finished with exit code 0
As you can see, the test passes when I'm expecting a bad request status, but I've put in logging and I know that the ResponseBody I'm sending back has a 200 status. Like I said, this is my first time with MockMvc, so I assume I've not set something up right. Any suggestions?
An HttpMessageNotReadableException is
Thrown by HttpMessageConverter implementations when the read method
fails.
You also get a 400 Bad Request in your response. This should all tell you that you are not sending what your server is expecting. What is your server expecting?
#RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
#ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(#RequestBody PaymentRequest paymentRequest, HttpServletRequest request, HttpServletResponse response, BindingResult result) throws Exception{
The main thing here is the #RequestBody annotated parameter. So you are telling your server to try and deserialize a PaymentRequest instance from the body of the HTTP POST request.
So let's see the request you are making
mockMvc.perform(post("/requestpayment")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest());
I don't see you providing a body to the request. There should be a content(String) call somewhere in there to set the content of the POST request. This content should be a JSON serialization of a PaymentRequest.
Note that because you are using the StandaloneMockMvcBuilder, you might need to set the HttpMessageConverter instances yourself, ie. a MappingJackson2HttpMessageConverter to serialize and deserialize JSON.
Note that the BindingResult parameter should come immediately after the parameter to which it's related. Like so
#RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
#ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(#Valid #RequestBody PaymentRequest paymentRequest, BindingResult result, HttpServletRequest request, HttpServletResponse response) throws Exception{
Don't forget the #Valid.
Note that this
requestPaymentController.setLoginService(mockLoginService);
requestPaymentController.handleRequestPayment(mockPaymentRequest, mockHttpServletRequest, mockHttpServletResponse, mockBindingResult);
is completely unrelated to the MockMvc test you are doing.
In my case, as sprint mvc w/ jackson (jackson-mapper-asl, v-1.9.10) deserialization requires JSON parser. And jackson requires a default constructor for http request message deserialization, if there's no default constructor, jackson will have a problem w/ reflection and throws HttpMessageNotReadableException exception.
This is to say, all the classes/sub-classes which used as Request body, (in this case) requires a default constructor. This costed me a few moments after I tried adding custom converter and other suggestions I got in stackoverflow in vain.
Or you can add Custom Deserializer or Mixin annotation to avoid adding default constructor hierachically everywhere. as described here: http://blogs.jbisht.com/blogs/2016/09/12/Deserialize-json-with-Java-parameterized-constructor. Check this if you're interested.
Seems duplicated here > Spring HttpMessageNotReadableException.
Make sure of the following:
return object implements Serializable
#ResponseBody annotation used on the controller method
On your unit test
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {....})
#WebMvcTest
#AutoConfigureMockMvc
Probably too late to answer but just in case someone is still looking at this page.
As #Sotirios Delimanolis mentions, the problem is due to a bad request - a '#RequestBody' is specified in the parameter but never supplied in the request body. So, if you add that to request using 'content(someRequestString)' as below, it should work.
PaymentRequest paymentRequest = new PaymentRequest(...);
String requestBody = new ObjectMapper().valueToTree(paymentRequest).toString();
mockMvc.perform(post("/requestpayment")
.content(requestBody)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("SUCCESS"))
.andExpect(jsonPath("$.paymentAmount", is(20)));
jsonPath may be used to verify the attributes on the response. In the above example, say PaymentResponse has attributes status and paymentAmount in the json response. These parts can be verified easily.
You may run into errors like -
NoClassDefFoundError: com/jayway/jsonpath/Predicate
while using jsonPath. So, make sure it is added to classpath explicitly as it is an optional dependency in spring-test and will not be available transitively. If using maven, do this:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>

Resources