POST method test in spring. Code 400 instead of 201 - java

I am creating an application where I can create a car object.
Car class without setters and getters:
package github.KarolXX.demo.model;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
#Entity
#Table(name = "cars")
public class Car {
#Id
#GeneratedValue(generator = "inc")
#GenericGenerator(name = "inc", strategy = "increment")
private int id;
#NotBlank(message = "car name`s must be not empty")
private String name;
private LocalDateTime productionYear;
private boolean tested;
public Car() {
}
public Car(#NotBlank(message = "car name`s must be not empty") String name, LocalDateTime productionYear) {
this.name = name;
this.productionYear = productionYear;
}
}
I would like to know how to test the POST method in Spring. Below is a code snippet for the POST method which just create Java object named Car (first snippet)
#PostMapping("/cars")
ResponseEntity<Car> createCar(#RequestBody #Valid Car newCar) {
logger.info("Creating new car");
var result = repository.save(newCar);
return ResponseEntity.created(URI.create("/" + result.getId())).body(result);
}
I am trying to test it this way:
package github.KarolXX.demo.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import github.KarolXX.demo.TestConfiguration;
import github.KarolXX.demo.model.Car;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.time.LocalDateTime;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#SpringBootTest
#AutoConfigureMockMvc
#ActiveProfiles("integration")
class CarControllerIntegrationServerSideTest {
#Autowired
private MockMvc mockMvc;
#Test
public void httpPost_createsNewCar_returnsCreatedCar() throws Exception {
//given
Car car = new Car("second server side test", LocalDateTime.parse("2021-01-02T13:34:54"));
//when + then
mockMvc.perform(post("/cars")
.content(asJsonString(car))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isCreated());
}
public static String asJsonString(final Car objectCar) {
try {
return new ObjectMapper().writeValueAsString(objectCar);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
I did it based on this article
Unfortunately my test does not pass. Although in the logs I can see that the request body and the request headers are set correctly, I get the status 400
2021-03-26 12:05:01.532 WARN 10696 --- [ main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Expected array or string.; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string.
at [Source: (PushbackInputStream); line: 1, column: 59] (through reference chain: github.KarolXX.demo.model.Car["productionYear"])]
MockHttpServletRequest:
HTTP Method = POST
Request URI = /cars
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"279"]
Body = {"id":0,"name":"second server side test","productionYear":{"month":"JANUARY","dayOfWeek":"SATURDAY","dayOfYear":2,"nano":0,"year":2021,"monthValue":1,"dayOfMonth":2,"hour":13,"minute":34,"second":54,"chronology":{"id":"ISO","calendarType":"iso8601"}},"tested":false,"brand":null}
Session Attrs = {}
Handler:
Type = github.KarolXX.demo.controller.CarController
Method = github.KarolXX.demo.controller.CarController#createCar(Car)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
MockHttpServletRequest:
HTTP Method = POST
Request URI = /cars
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"279"]
Body = {"id":0,"name":"second server side test","productionYear":{"month":"JANUARY","dayOfWeek":"SATURDAY","dayOfYear":2,"nano":0,"year":2021,"monthValue":1,"dayOfMonth":2,"hour":13,"minute":34,"second":54,"chronology":{"id":"ISO","calendarType":"iso8601"}},"tested":false,"brand":null}
Session Attrs = {}
Handler:
Type = github.KarolXX.demo.controller.CarController
Method = github.KarolXX.demo.controller.CarController#createCar(Car)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status expected:<201> but was:<400>
Expected :201
Actual :400
I have no idea what could be wrong but when I change this line of code mockMvc.perform(post("/cars") to this one mockMvc.perform(post("/") then I got status 404

I have solved the problem, but I don't know why the previous version is not working. I removed the static method from the test class and changed the POST method checking: namely I created a variable holding JSON as a String and passed it to the content method. In previous version this String was returned by ObjectMapper().writeValueAsString() in static method. My modified test class:
#Test
public void httpPost_createsNewCar_returnsCreatedCar() throws Exception {
//given
//Car car = new Car("second server side test", LocalDateTime.parse("2021-01-02T13:34:54"));
String id = String.valueOf(repo.getSize());
String jsonString = new JSONObject()
.put("id", id)
.put("tested", false)
.put("productionYear", "2017-06-18T12:12:12")
.put("name", "Toyota")
.toString();
//when + then
mockMvc.perform(post("/cars")
.content(jsonString)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isCreated());
}

Related

MockMVC test Status expected:<201> but was:<404>

import com.google.gson.Gson;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import preproject.underdog.answer.dto.answer.AnswerPostDto;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#WebMvcTest(value = AnswerController.class)
//#AutoConfigureRestDocs
#MockBean(JpaMetamodelMappingContext.class)
public class AnswerController {
#Autowired
private MockMvc mockMvc;
#Autowired
private Gson gson;
#Test
#DisplayName("답변 글 작성 테스트")
void postAnswer() throws Exception {
AnswerPostDto post = new AnswerPostDto("테스트", 1L, 1L);
String content = gson.toJson(post);
ResultActions actions =
mockMvc.perform(
post("/answers")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(content)
);
actions
.andExpect(status().isCreated());
}
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import preproject.underdog.answer.dto.answer.AnswerPostDto;
import preproject.underdog.answer.dto.answer.AnswerRespDto;
import preproject.underdog.answer.mapper.AnswerMapper;
import preproject.underdog.answer.service.AnswerService;
import javax.validation.Valid;
import java.time.LocalDateTime;
#RestController
#RequestMapping("/answers")
#Validated
#RequiredArgsConstructor
public class AnswerController {
private final AnswerService answerService;
private final AnswerMapper answerMapper;
#PostMapping
public ResponseEntity postAnswer(#Valid #RequestBody AnswerPostDto answerPostDto) {
AnswerRespDto answerRespDto = new AnswerRespDto(1L,"테스트",1L,1L,1L, LocalDateTime.of(2023, 4, 3, 3, 3, 0),LocalDateTime.of(2023, 4, 3, 3, 3, 0));
return new ResponseEntity<>(answerRespDto, HttpStatus.CREATED);
}
Here's my codes first,
I tried make Rest Docs first, but I faced a lot of errors, so I just started to try pass the MockMVC test first.
So I gotta got 201 response from that, but I only got 404 error
here's my log.
MockHttpServletRequest:
HTTP Method = POST
Request URI = /answers
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"49"]
Body = {"content":"테스트","userId":1,"questionId":1}
Session Attrs = {}
Handler:
Type = org.springframework.web.servlet.resource.ResourceHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 404
Error message = null
Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
#PostMapping
public ResponseEntity postAnswer(#Valid #RequestBody AnswerPostDto answerPostDto) {
return new ResponseEntity<>(HttpStatus.CREATED);
}
I even tried to changed the code to get only 201 response like this
but it still gave me 404 error
What should I do?? thanks

Does the openapi springboot server generator have a broken threading model?

So we're trying to use the OpenAPI generator and so far we've had mixed results.
Steps to reproduce:
Download openapi generator jar: wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator/4.0.3/openapi-generator-4.0.3.jar
Generate springboot server for the petstore example: java -jar openapi-generator-cli-4.0.3.jar generate -g spring -i https://raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v3.0/petstore.yaml
You'll end up with controller classes that look like this:
package org.openapitools.api;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.NativeWebRequest;
import java.util.Optional;
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2019-08-06T15:08:49.070+01:00[Europe/London]")
#Controller
#RequestMapping("${openapi.swaggerPetstore.base-path:/v1}")
public class PetsApiController implements PetsApi {
private final NativeWebRequest request;
#org.springframework.beans.factory.annotation.Autowired
public PetsApiController(NativeWebRequest request) {
this.request = request;
}
#Override
public Optional<NativeWebRequest> getRequest() {
return Optional.ofNullable(request);
}
}
/**
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (4.0.3).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
package org.openapitools.api;
import org.openapitools.model.Error;
import org.openapitools.model.Pet;
import io.swagger.annotations.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2019-08-06T15:08:49.070+01:00[Europe/London]")
#Validated
#Api(value = "pets", description = "the pets API")
public interface PetsApi {
default Optional<NativeWebRequest> getRequest() {
return Optional.empty();
}
#ApiOperation(value = "Create a pet", nickname = "createPets", notes = "", tags={ "pets", })
#ApiResponses(value = {
#ApiResponse(code = 201, message = "Null response"),
#ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
#RequestMapping(value = "/pets",
produces = { "application/json" },
method = RequestMethod.POST)
default ResponseEntity<Void> createPets() {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
#ApiOperation(value = "List all pets", nickname = "listPets", notes = "", response = Pet.class, responseContainer = "List", tags={ "pets", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "A paged array of pets", response = Pet.class, responseContainer = "List"),
#ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
#RequestMapping(value = "/pets",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<List<Pet>> listPets(#ApiParam(value = "How many items to return at one time (max 100)") #Valid #RequestParam(value = "limit", required = false) Integer limit) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
ApiUtil.setExampleResponse(request, "application/json", "null");
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
#ApiOperation(value = "Info for a specific pet", nickname = "showPetById", notes = "", response = Pet.class, tags={ "pets", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Expected response to a valid request", response = Pet.class),
#ApiResponse(code = 200, message = "unexpected error", response = Error.class) })
#RequestMapping(value = "/pets/{petId}",
produces = { "application/json" },
method = RequestMethod.GET)
default ResponseEntity<Pet> showPetById(#ApiParam(value = "The id of the pet to retrieve",required=true) #PathVariable("petId") String petId) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
ApiUtil.setExampleResponse(request, "application/json", "null");
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
}
So my question is this: everything I can find implies that Spring Controllers are multi-threaded and may handle multiple requests at once. Is the code generator broken? Am I interpreting this completely wrong?
The constructor for PetsApiController gives me pause. If it's being autowired once per request then that implies that there's only one per request?
#org.springframework.beans.factory.annotation.Autowired
public PetsApiController(NativeWebRequest request) {
this.request = request;
}
The swagger code generators are notoriously bad, friend of mine said that the generators have the breadth but not the depth. You can generate skeletons for all sorts of languages and frameworks, but they have severe limitations. For example, try generating a good skeleton from a SwaggerDoc with Page<Something> or other Generics. I would very sadly say that they have almost no utility, and the tools tend to only work reliably the other way around, that is coding first and then generating the SwaggerDoc.
A place I worked at had a great concept I really liked whereby you would design your API first before implementing it, which sounds like you are trying to do. Some IDEs even support generated code, and there are plugins for build tools such as maven gradle etc to generate the code from your yaml.
But in practice I spent days trying to get desirable results from these tools and gave up. I think the real problem is Swagger/OpenAPI is still heavily viewed as documentation tool, not a design tool. I also think that trying to create an all encompassing project generator was setup to fail from the get-go.
I myself tried to customize the moustache templates which the generator used, but generics in Java were a nightmare, and you couldn't get the proper workflow working whereby I would change the SwaggerDoc and then update my code, as my approach was to generate an interface, and then implement that interface, but Annotations weren't inherited so I had to duplicate all the code anway meaning there was no benefit.

Spring Boot Test HttpMessageNotReadableException

I have one simple test to write for a POST method in a RestController. This post mapping works fine when i run the application. But, in it's test i always get HttpMessageNotReadableException with 400-Bad Request.
InternalCustomerController.class
#RestController
#RequestMapping(
value = "/",
consumes = {MediaType.APPLICATION_XML_VALUE},
produces = {MediaType.APPLICATION_XML_VALUE})
#RequiredArgsConstructor
public class InternalCustomerController {
private final CustomerService customerService;
#PostMapping(value = "/cpdupdate")
public Notification updateCustomerProduct(#NotNull #RequestBody Customer customer) {
return customerService.handleUpdateCustomerProduct(customer);
}
}
InternalCustomerControllerTest.class
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = InternalCustomerController.class)
#Import(TestObjectMapperConfig.class)
public class InternalCustomerControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CustomerService customerService;
#Test
public void testUpdateCustomerProduct() throws Exception {
Customer validCustomer = createValidCustomer("76d211bb680843c5b63afa1c13a0a5e5", "60");
when(this.customerService.handleUpdateCustomerProduct(any()))
.thenReturn(buildDummyNotification(messageId, "SUCCESS"));
mockMvc.perform(post("/cpdupdate")
.contentType(MediaType.APPLICATION_XML)
.content(objectToString(validCustomer)))
.andExpect(status().isOk()); // Fails Here as it gets 400 in return
}
// Customer Class Generated from Provided XSD on maven build
private static Customer createValidCustomer(String internalProductId, int customerId) {
Customer customer = new Customer();
customer.setCustomerId(customerId);
Customer.InternalProducts products = new Customer.InternalProducts();
products.getProductIds().add(internalProductId);
customer.setInternalProducts(products);
return customer;
}
private Notification buildDummyNotification(String messageId, String message) {
return Notification.builder()
.messageId(messageId)
.message(message)
.build();
}
private static String objectToString(Object element) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
JAXBContext context = JAXBContext.newInstance(element.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(element, outputStream);
return outputStream.toString("UTF-8");
} catch (JAXBException | IOException ex) {
log.error(ex.getMessage(), ex);
return "";
}
}
}
Error message contains JSON parse error: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token. So i tried adding a configuration for ObjectMapper and Import it into the test.
#Configuration
public class TestObjectMapperConfig {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
return objectMapper;
}
}
But i still get the error.
What i am missing here?
Full Error Message
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
at [Source: java.io.PushbackInputStream#53311681; line: 4, column: 49] (through reference chain: com.clps.Customer["internalProducts"]->com.clps.Customer$InternalProducts["productId"])]
MockHttpServletRequest:
HTTP Method = POST
Request URI = /cpdupdate
Parameters = {}
Headers = {Content-Type=[application/xml]}
Handler:
Type = com.pt.controllers.InternalCustomerController
Method = public com.pt.models.Notification com.pt.controllers.InternalCustomerController.updateCustomerProduct(com.clps.Customer)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status
Expected :200
Actual :400

Rest client data into Java object. Getting com.fasterxml.jackson.databind.exc.MismatchedInputException

I have a sample backend response coming as below:
When I try to map this response into the java object, I am getting following error.
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of com.mc.membersphere.model.MemberSummaryLabel[] out of START_OBJECT token
Seems like the issue with the body tag coming from API. Which has array of objects. I need help, how to handle this body tag arrays value in Java mapping?
Backend API Response:
{
"body": [{
"pcp": "KASSAM, Far",
"er12M": "0",
"ipAdmits12M": "0",
"ipReAdmits12M": "0",
"rx12M": "0",
"pastMedicalHistory": " ",
"erCost12M": "0.0"
}
]
}
Java Program to get the Rest data into the Java objects is as below.
import java.util.Collections;
import java.util.Properties;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.mc.membersphere.model.MemberSummaryLabel;
import com.mc.membersphere.utility.PropertyUtil;
public class TestRestclient implements CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(TestApi.class, args); }
private static Properties prop = PropertyUtil.getProperties();
#Override
public void run(String... args) throws Exception {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
HttpEntity<String> entity = new HttpEntity<String>(headers);
String getMVPSummaryUrl = prop.getProperty("getmvpmembersummary.url");
String url = getMVPSummaryUrl+"/"+"CA";
ResponseEntity<MemberSummaryLabel[]> response = restTemplate.exchange(url, HttpMethod.GET,entity, MemberSummaryLabel[].class);
if(response.getStatusCode()== HttpStatus.OK) {
for(MemberSummaryLabel memberSummaryLabel : response.getBody())
{
System.out.println(memberSummaryLabel.pcp);
}
//System.out.println("Print response" + response);
}
else {
System.out.println("Error");
}
}
}
MemberSummaryLabel is as below.
import com.fasterxml.jackson.annotation.JsonProperty;
public class MemberSummaryLabel {
#JsonProperty("pcp")
public String pcp;
#JsonProperty("er12M")
public Integer er12M;
#JsonProperty("ipAdmits12M")
public Integer ipAdmits12M;
#JsonProperty("ipReAdmits12M")
public Integer ipReAdmits12M;
#JsonProperty("rx12M")
public Integer rx12M;
#JsonProperty("pastMedicalHistory")
public String pastMedicalHistory;
#JsonProperty("erCost12M")
public Double erCost12M;
}
I see, its an issue with your mapping. Your response is in "body" and body contains list of MemberSummaryLabel. So, you need to have one more class as mentioned below,
public class Body{
#JsonProperty("body")
public List<MemberSummaryLabel> memberSummaryLabelList;
}
And your exchange method should return NewClass.
ResponseEntity<Body> response = restTemplate.exchange(url, HttpMethod.GET,entity, Body.class);
And for, iteration use,
for(MemberSummaryLabel memberSummaryLabel : response.getBody().getMemberSummaryLabelList()){
}

How to check JSON in response body with mockMvc

This is my method inside my controller which is annotated by #Controller
#RequestMapping(value = "/getServerAlertFilters/{serverName}/", produces = "application/json; charset=utf-8")
#ResponseBody
public JSONObject getServerAlertFilters(#PathVariable String serverName) {
JSONObject json = new JSONObject();
List<FilterVO> filteredAlerts = alertFilterService.getAlertFilters(serverName, "");
JSONArray jsonArray = new JSONArray();
jsonArray.addAll(filteredAlerts);
json.put(SelfServiceConstants.DATA, jsonArray);
return json;
}
I am expecting {"data":[{"useRegEx":"false","hosts":"v2v2v2"}]} as my json.
And this is my JUnit test:
#Test
public final void testAlertFilterView() {
try {
MvcResult result = this.mockMvc.perform(get("/getServerAlertFilters/v2v2v2/").session(session)
.accept("application/json"))
.andDo(print()).andReturn();
String content = result.getResponse().getContentAsString();
LOG.info(content);
} catch (Exception e) {
e.printStackTrace();
}
}
Here is the console output:
MockHttpServletResponse:
Status = 406
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Even result.getResponse().getContentAsString() is an empty string.
Can someone please suggest how to get my JSON in my JUnit test method so that I can complete my test case.
I use TestNG for my unit testing. But in Spring Test Framework they both looks similar. So I believe your test be like below
#Test
public void testAlertFilterView() throws Exception {
this.mockMvc.perform(get("/getServerAlertFilters/v2v2v2/").
.andExpect(status().isOk())
.andExpect(content().json("{'data':[{'useRegEx':'false','hosts':'v2v2v2'}]}"));
}
If you want check check json Key and value you can use jsonpath
.andExpect(jsonPath("$.yourKeyValue", is("WhatYouExpect")));
You might find thatcontent().json() are not solveble please add
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
The 406 Not Acceptable status code means that Spring couldn't convert the object to json. You can either make your controller method return a String and do return json.toString(); or configure your own HandlerMethodReturnValueHandler. Check this similar question Returning JsonObject using #ResponseBody in SpringMVC
You can try the below for get and post methods
#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 testPostMethod 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() methods 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);
}
There are 2 ways to check JSON responses. Lemme guide you through both of them, (taking test method from the question above, and assuming response {"data":[{"useRegEx":"false","hosts":"v2v2v2"}]} as given above)
Method 1) Asserting complete JSON
#Test
public final void testAlertFilterView() {
mockMvc.perform(get("/getServerAlertFilters/v2v2v2/")
.contentType("application/json"))
.andExpect(status().isOk())
// you may even read bigger json responses from file and convert it to string, instead of simply hardcoding it in test class
.andExpect(content().json("{"data":[{"useRegEx":"false","hosts":"v2v2v2"}]}"))
}
Method 2) Asserting specific key-value of response (not writing redundant piece of code)
.andExpect(jsonPath("$.data[0].useRegEx").value(false))
.andExpect(jsonPath("$.data[0].hosts").value("v2v2v2"));
Another thing you might need is the import statement,
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
If you want to check a few values in a specific field of JSON
.andExpect(MockMvcResultMatchers.jsonPath("$.message",
AllOf.allOf(
StringContains.containsString("name: must not be null"),
StringContains.containsString("type: must not be null")
)));
How it looks in the test class. JUnit4.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hamcrest.core.AllOf;
import org.hamcrest.core.StringContains;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
#RunWith(MockitoJUnitRunner.class)
public class YourControllerTest {
#Mock
private YourService service;
private MockMvc mvc;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders
.standaloneSetup(new YourController(service))
.setControllerAdvice(new YourExceptionHandler())
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.build();
}
#Test
public void yourControllerMethodName_400_validation() throws Exception {
String path = "/orders/{orderId}/items";
Integer orderId = 123;
YourRequestDto requestDto = YourTestFactory.buildYourRequestDto();
requestDto.setName(null);
requestDto.setType(null);
YourResponseDto expected = YourTestFactory.buildYourResponseDto(requestDto);
Mockito
.when(service.someMethod(orderId, requestDto))
.thenReturn(expected);
mvc
.perform(
MockMvcRequestBuilders.post(path, orderId)
.contentType(MediaType.APPLICATION_JSON)
.content(new ObjectMapper().writeValueAsString(requestDto))
)
.andExpect(MockMvcResultMatchers.status().isBadRequest())
.andExpect(MockMvcResultMatchers.jsonPath("$.message",
AllOf.allOf(
StringContains.containsString("name: must not be null"),
StringContains.containsString("type: must not be null")
)));
}
}

Categories

Resources