MockMvc PostRequest Exception - spring-boot

I have following post mapping.
#PostMapping(value = BULK_UPDATE)
#ApiOperation(value = "Bulk Update of Markets by pairs of Market Key and Tier Quantity Id", tags = "Bulk", code = 200)
#ApiImplicitParams({
#ApiImplicitParam(name = "MarketTierQuantityId", value = "List of Market Key and Tier Quantity Id pairs",
paramType = "body", allowMultiple = true, dataType = "MarketTierQuantityId", required = true) })
#ApiResponses({
#ApiResponse(code = 200, message = "Bulk update successful", response = MarketStatus.class, responseContainer = "List") })
#ResponseStatus(org.springframework.http.HttpStatus.OK)
public ResponseEntity<StreamingResponseBody> bulkUpdate(
#RequestParam(name = IGNORE_SYNC_PAUSE_FAILURE, required = false, defaultValue = "false")
#ApiParam(name = IGNORE_SYNC_PAUSE_FAILURE, value = "Ignore failure of the jobs pause command") boolean ignoreJobsPauseFailure,
#RequestBody #ApiParam(name = "MarketTierQuantityId", value = "List of Market Key and Tier Quantity Id pairs", required = true) List<MarketTierQuantityId> marketTierQuantities,
#RequestParam(name = MOVE_TO_PREAUTH_FLAG, required = false, defaultValue = "true")
#ApiParam(name = MOVE_TO_PREAUTH_FLAG, value = "Move new units to Preauth for the markets with active waitlists") boolean moveToPreauth) throws BusinessException {
String requestId = getRequestId();
boolean jobsPaused = pauseJobs(ignoreJobsPauseFailure);
return LoggingStopWatch.wrap(() -> {
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON)
.body(outputStream -> process(new SyncBulkProcessorHelper(outputStream),
marketTierQuantities, jobsPaused, requestId, moveToPreauth, LoggingStopWatch.create(LOGGER, "Bulk Update")));
});
}
and i have written the following test.
#RunWith(SpringRunner.class)
#WebMvcTest(BulkUpdateController.class)
#ContextConfiguration(classes = { BulkUpdateController.class, SharedExecutor.class })
#ActiveProfiles("dev")
public class BulkUpdateControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private BulkStatusService bulkStatusService;
#MockBean
private BulkMarketService bulkMarketService;
#MockBean
private HttpService httpService;
#MockBean
private RestClient restClient;
#MockBean
private BulkProcessorHelper helper;
#Test
public void test() throws Exception {
String request = TestHelper.getSerializedRequest(getBulkUpdateRequest(), MarketTierQuantityId.class);
mockMvc.perform(post("/bulkupdate").accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON)
.content(request)).andExpect(status().is4xxClientError());
}
public MarketTierQuantityId getBulkUpdateRequest() {
MarketTierQuantityId market = new MarketTierQuantityId();
market.setMarketKey("00601|PR|COBROKE|POSTALCODE|FULL");
market.setTierQuantityId("10");
return market;
}
Getting the following error, have tried every possible way to resolve it but doesnt help.
Request failed. Error response:
{\"responseStatus\":{\"errorCode\":\"BadRequest\",\"message\":\"JSON
parse error: Cannot deserialize instance of java.util.ArrayList out
of START_OBJECT token\",\"stackTrace\":\"BusinessException(JSON parse
error:
P.S -> new to JUnits and mocks

Related

WebFlux API-Layer Test returns 404

I'm trying to get started with Spring WebFlux with Spring Boot 3.0
I'm Building a Person API with an open api generator.
The Application runs and gives the expected results when it is tested manually.
But I'm not able to get the API layer unit tested.
This is my Test Class
#WebFluxTest(controllers = {PersonApiController.class})
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class PersonRouterTest {
#MockBean
private PersonService personService;
#Autowired
private WebTestClient client;
#ParameterizedTest
#CsvSource({"1234, Max Mustermann", "5678, Erika Musterfrau"})
void retrieve_a_name(String id, String name) {
when(personService.getPersonDataByID(1234)).thenReturn(Mono.just(new PersonData(1234, "Max Mustermann")));
when(personService.getPersonDataByID(5678)).thenReturn(Mono.just(new PersonData(5678, "Erika Musterfrau")));
client.get()
.uri(uriBuilder -> uriBuilder
.path("/persons/{id}")
.build(id))
.accept(MediaType.ALL)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.name").isEqualTo(name);
}
This is my Controller Class
#Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2022-12-
09T09:14:36.692713900+01:00[Europe/Vienna]")
#Controller
#RequestMapping("${openapi.openAPIDefinition.base-path:}")
public class PersonApiController implements PersonApi {
private final PersonApiDelegate delegate;
public PersonApiController(#Autowired(required = false) PersonApiDelegate delegate) {
this.delegate = Optional.ofNullable(delegate).orElse(new PersonApiDelegate() {});
}
#Override
public PersonApiDelegate getDelegate() {
return delegate;
}
}
The API interface:
#Tag(
name = "Person",
description = "the Person API"
)
public interface PersonApi {
default PersonApiDelegate getDelegate() {
return new PersonApiDelegate() {
};
}
#Operation(
operationId = "findPersonById",
summary = "Find Person by ID",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operation",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.GET},
value = {"/persons/{id}"},
produces = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> findPersonById(#Parameter(name = "id",description = "Person ID",required = true) #PathVariable("id") Integer id, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().findPersonById(id, exchange);
}
#Operation(
operationId = "savePerson",
summary = "Creates a new Person",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operatoin",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.POST},
value = {"/persons"},
produces = {"application/json"},
consumes = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> savePerson(#Parameter(name = "PersonData",description = "") #RequestBody(required = false) Mono<PersonData> personData, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().savePerson(personData, exchange);
}
}
and finally my delegate impl:
#Service
public class PersonDelegateImpl implements PersonApiDelegate {
public static final Mono<ResponseEntity<?>> RESPONSE_ENTITY_MONO = Mono.just(ResponseEntity.notFound().build());
private final PersonService service;
private final PersonMapper mapper;
public PersonDelegateImpl(PersonService service, PersonMapper mapper) {
this.service = service;
this.mapper = mapper;
}
public static <T> Mono<ResponseEntity<T>> toResponseEntity(Mono<T> mono) {
return mono.flatMap(t -> Mono.just(ResponseEntity.ok(t)))
.onErrorResume(t -> Mono.just(ResponseEntity.internalServerError().build()));
}
#Override
public Mono<ResponseEntity<PersonData>> findPersonById(Integer id, ServerWebExchange exchange) {
Mono<com.ebcont.talenttoolbackend.person.PersonData> personDataByID = service.getPersonDataByID(id);
return toResponseEntity(personDataByID.map(mapper::map));
}
#Override
public Mono<ResponseEntity<PersonData>> savePerson(Mono<PersonData> personData, ServerWebExchange exchange) {
return PersonApiDelegate.super.savePerson(personData, exchange);
If I run the test class I always get:
< 404 NOT_FOUND Not Found
< Content-Type: [application/json]
< Content-Length: [139]
{"timestamp":"2022-12-09T08:45:41.278+00:00","path":"/persons/1234","status":404,"error":"Not Found","message":null,"requestId":"4805b8b8"}
I have tried to change the Context Configuration but I did not get it to work.
I found the Problem, changing the Test Config to :
#WebFluxTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class, PersonApiController.class, PersonDelegateImpl.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
Solved my Problem.
The Controller bean was not recognized. I had to add PersonApiCrontroller and PersonDelegateImpl to the Context Config. i then removed the PersonApiController from the #WebFluxTest annotation.

OpenApi Swagger not showing Exception as a response

I'm using OpenApi Swagger UI (v. 4.14) with SpringBoot. I'm getting all the info I need with the Swagger, except for the exception. Here's my code.
Garage class:
#Schema(description = "Details about the Car")
#Document("Garage")
public class Garage implements Serializable {
#Schema(description = "An ID of the car in the database", accessMode = Schema.AccessMode.READ_ONLY)
#Id
private String id;
#Schema(description = "The name of the car")
#Field("model")
protected String carModel;
#Schema(description = "Car's engine power output")
protected Integer hp;
#Schema(description = "Production year of the car")
#Field("Year")
protected Integer year;
#Schema(description = "The name of car's designer")
protected String designer;
// controllers, getters, setters, toString
Controller:
// some other code
#Operation(summary = "Deletes a car by its id")
#ApiResponses(value = {
#ApiResponse(responseCode = "200",
description = "A car is deleted from the Garage",
content = {#Content(
schema = #Schema(implementation = Garage.class),
mediaType = "application/json")}),
#ApiResponse(responseCode = "404",
description = "A car with this id is not in our garage",
content = #Content(
schema = #Schema(implementation = RestExceptionHandler.class),
mediaType = "application/json"))})
#DeleteMapping(path = "/deleteCar/{carId}")
public void deleteCarFromGarage(#PathVariable("carId") String id) {
garageService.deleteFromGarage(id);
}
// some other code
Exception handler:
#Schema(description = "Exception handling")
#RestControllerAdvice
public class RestExceptionHandler {
#Schema(description = "The ID is not valid")
#ExceptionHandler(value = {IllegalArgumentException.class})
public ResponseEntity<Object> resourceNotFoundException(IllegalArgumentException exception) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(exception.getMessage());
}
}
I'm getting 200 responses every time, no matter do I delete a correct id, or an incorrect one.
EDIT: here's my deleteFromGarage method
public void deleteFromGarage(String id) {
garageRepository.deleteById(id);
}
I've edited my deleteFromGarage method, and that solved the issue
deleteFromGarage before:
public void deleteFromGarage(String id) {
garageRepository.deleteById(id);
}
deleteFromGarage now:
public void deleteFromGarage(String id) {
if (garageRepository.findById(id).isEmpty()) {
throw new IllegalArgumentException("The ID is not valid");
} else {
garageRepository.deleteById(id);
}
}

unable to get the response entity from an api with mockmvc

I have an issue when I try to run my controller's unit test class. I get always a empty body in the response and I don't manage to find why.
I put here the code. Maybe someone with an external vision will be able to see the reason.
the controller:
#ResponseBody
#PostMapping(path = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> uploadFile(#RequestParam("file") MultipartFile multipartFileData, #RequestParam(name="jobId", required = false) String jobId) {
JobStatus result;
try {
result = this.fileService.uploadFileChunk(multipartFileData, 1, 1, jobId);
}catch (ExecutionException|InterruptedException|IOException ex){
Thread.currentThread().interrupt();
return new ResponseEntity<>(ex,HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(result,HttpStatus.OK);
}
the unit test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes= FileUploadServiceRestController.class)
public class FileUploadServiceControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#MockBean
private FileUploadServiceImpl fileService;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void testUploadFile()
throws Exception {
MockMultipartFile file
= new MockMultipartFile(
"file",
"hello.txt",
MediaType.TEXT_PLAIN_VALUE,
"Hello, World!".getBytes()
);
JobStatus job = new JobStatus("uuid", ConstantUtil.JOB_STARTED);
when(fileService.uploadFileChunk(Mockito.any(MultipartFile.class),Mockito.eq(1),Mockito.eq(1),Mockito.isNull())).thenReturn(job);
mockMvc.perform(MockMvcRequestBuilders.multipart("/file/upload").file(file))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}
and the object which will be transfered:
public class JobStatus implements Serializable {
private static final long serialVersionUID = -4405865740177389860L;
private String jobId;
private String status;
public JobStatus() {
}
public JobStatus(String jobId, String status) {
this.jobId = jobId;
this.status = status;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
For information, this controller works well when I call it from the client. I can see that the mock is well returned when I put a breakpoint at the end of the controller, but the response body stay empty.
I add here the result of print if it could help:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /file/upload
Parameters = {}
Headers = [Content-Type:"multipart/form-data"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.iso.fileservice.controller.FileUploadServiceRestController
Method = org.iso.fileservice.controller.FileUploadServiceRestController#uploadFile(MultipartFile, String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
thanks, Mathieu
Just ran into this today.
I found the answer on Why MockMvc request retrieve empty responseBody while test succeed?
I just added My restController with #Autowired instead of #InjectMocks, after that I started to retrieve the Response Entity instead of a 500 status response

ServiceResponse mocked which gives a null value and not expected this null

I'm writing j-unit Test-cases for my services and in which i couldn't mock service Response properly, Which is giving me a null. can somebody help me in this issue.
public ResponseEntity<Void> lockGet(
#ApiParam(value = "Unique identifier for this request.", required = true) #RequestHeader(value = "service-id", required = true) String serviceId,
#ApiParam(value = "Logged in userid.", required = true) #RequestHeader(value = "user-id", required = true) String userId,
#ApiParam(value = "Unique messageid.", required = true) #RequestHeader(value = "message-id", required = true) String messageId,
#RequestHeader(value = "access-token", required = true) String accessToken,
#ApiParam(value = "Unique id of the doamin of the entity", required = true) #RequestParam(value = "lockDomainId", required = true) Long lockDomainId,
#ApiParam(value = "Unique id of the entity to be fetched", required = true) #RequestParam(value = "lockEntityId", required = true) Long lockEntityId,
HttpServletRequest request, HttpServletResponse response) {
ResponseEntity<Void> result = null;
if (request.getAttribute("user-id") != null)
userId = (String) request.getAttribute("user-id");
String logContext = "||" + lockDomainId + "|" + lockEntityId + "||";
ThreadContext.put("context", logContext);
long t1 = System.currentTimeMillis();
LOG.info("Method Entry: lockGet" + logContext);
ServiceRequest serviceRequest = AppUtils.mapGetRequestHeaderToServiceRequest(serviceId, userId, lockDomainId,
lockEntityId);
try {
ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
// set all the response headers got from serviceResponse
HeaderUtils.setResponseHeaders(serviceResponse.getResponseHeaders(), response);
result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
} catch (Exception ex) {
LOG.error("Error in lockGet", ex);
result = new ResponseEntity<Void>(HttpStatus.INTERNAL_SERVER_ERROR);
}
ThreadContext.put("responseTime", String.valueOf(System.currentTimeMillis() - t1));
LOG.info("Method Exit: lockGet");
return result;
}
#Test
public void testLockGetForError() {
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
LockService service = Mockito.mock(LockService.class);
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
I tried in different scenario's which couldn't fix this issue. Can someone help me out. Thanks in advance.
From the code that you have put , the issue that i see is that you are actually mocking the LockService object but when calling the lockApiController.lockGet method the code is not actually working with the mocked LockService since lockApiController has an LockService object of it's own.
One way to solve this issue is to inject the mocked LockService
object into the lockApiController object using #Spy. This way
when the getLock() is called it will be actually called on the
mocked object and will return the mock response provided.
So in your test :
#Test
public void testLockGetForError() {
LockService service = Mockito.mock(LockService.class);
LockApiController lockApiController = Mockito.spy(new LockApiController(service));
request.setAttribute("user-id","TestUser");
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setUserId("TestUser");
ServiceResponse serviceResponse = new ServiceResponse();
when(service.getLock(serviceRequest)).thenReturn(serviceResponse);
// ServiceResponse serviceResponse = lockService.getLock(serviceRequest);
ResponseEntity<Void> result = new ResponseEntity<Void>(HeaderUtils.getHttpStatus(serviceResponse));
ResponseEntity<Void> lockGet = lockApiController.lockGet("1234", "TestUser", "TestMessage", "TestTkn", 12345L, 12345L, request, response);
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, lockGet.getStatusCode());
}
So you can try passing the mocked LockService object to the spy object.
Another way is to try using the #InjectMocks to inject the mocked
object into the LockApiController.
#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
For example:
#Mock
Map<String, String> wordMap;
#InjectMocks
MyDictionary dic = new MyDictionary();
#Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", dic.getMeaning("aWord"));
}
For the class:
public class MyDictionary {
Map<String, String> wordMap;
public MyDictionary() {
wordMap = new HashMap<String, String>();
}
public void add(final String word, final String meaning) {
wordMap.put(word, meaning);
}
public String getMeaning(final String word) {
return wordMap.get(word);
}
}
For both of these to work , you must be having a constructor or appropriate setters to set the mock object to the LockApiController class.
Reference : https://howtodoinjava.com/mockito/mockito-annotations/

Spring controller getting bodyRequest null

I am facing a problem while trying to process a POST request via POSTMAN.
In my controller I have :
#ApiOperation(value = "xxxx", notes = "xxxx", response =
String.class, authorizations = {
#Authorization(value = "basicAuth")
}, tags={ "saveCourse", })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "successful operation", response =
String.class),
#ApiResponse(code = 404, message = "Not found", response =
String.class),
#ApiResponse(code = 405, message = "Invalid input", response =
String.class),
#ApiResponse(code = 500, message = "Internal Server Error", response =
String.class),
#ApiResponse(code = 200, message = "unexpected error", response =
String.class) })
#RequestMapping(value = "/course/saveCourse",
produces = { "application/json"},
consumes = { "application/json"},
method = RequestMethod.POST)
ResponseEntity<String> saveCourse(#ApiParam(value = "xxxxx" ,required=true ) #RequestBody Course coure){
LOG.info(course.toString);
}
Class Course :
public class Course implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#JsonProperty("prof")
private Prof prof = null;
#JsonProperty("students")
private List<Strudent> students = new ArrayList<Strudent>();
// getters & setters
// ...
}
class Prof :
public class Prof implements Serializable {
#JsonProperty("profLastName")
private String profLastName = null;
#JsonProperty("profFirstName")
private String profFirstName = null;
#JsonProperty("age")
private int age = null;
// getters & setters
}
class Student :
public class Student implements Serializable {
#JsonProperty("studentId")
private String studentId = null;
#JsonProperty("studentName")
private String studentName = null;
#JsonProperty("studAge")
private int studAge = null;
// getters & setters
// ...
}
in POSTMAN I am sending a POST request with the header :
Content-Type : application/json
the body :
{
"prof": {
"profLastName":"test",
"profFirstName":"test",
"age":"30"
},
"students" :[
"{'studentId':'0','studentName':'','studAge':'00'}",
"{'studentId':'2','studentName':'','studAge':'21'}",
"{'studentId':'4','studentName':'','studAge':'40'}",
"{'studentId':'6','studentName':'','studAge':'60'}"
]
}
When I process the request I am getting the RequestBody null :
[http-nio-xxxx-exec-4] INFO com.test.myControllerIml - class Course {
prof: null
students: []
}
you request body is wrong
you should use
{
"prof": {
"profLastName":"test",
"profFirstName":"test",
"age":"30"
},
"students" :[
{"studentId":"0","studentName":"","studAge":"00"},
{"studentId":"2","studentName":"","studAge":"21"},
{"studentId":"4","studentName":"","studAge":"40"},
{"studentId":"6","studentName":"","studAge":"60"}
]
}

Resources