How to post a date as a parameter using MockHttpServletRequestBuilder with jUnit? - spring

I've been able to do tests normally with MockHttpServletRequestBuilder while I was only posting strings as parameters.
How can I post a date as a parameter, as it seems that my FormattingConversionService below does not work into jUnit tests, and my spring infrastructure is not converting String values to Calendar ones.
#Bean
public FormattingConversionService mvcConversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
DateFormatterRegistrar formatterRegistrar = new DateFormatterRegistrar();
formatterRegistrar.setFormatter(new DateFormatter("yyyy-MM-dd"));
formatterRegistrar.registerFormatters(conversionService);
return conversionService;
}
The test I'm trying to do is:
#Test #Transactional
public void shouldSaveABill() throws Exception {
String dueDate = "05/25/2017";
MockHttpServletRequestBuilder request = MockMvcRequestBuilders
.post("/saveBill")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("id", "1")
.param("description", "Food")
.param("dueDate", "05/25/2017")
.param("value", "150");
mockMvc.perform(request)
.andExpect(MockMvcResultMatchers.status().is(302))
.andExpect(MockMvcResultMatchers.forwardedUrl("/someUrl"));
}
While debugging the test, I got the error:
Field error in object 'bill' on field 'dueDate': rejected value [05/25/2017]; codes [typeMismatch.bill.dueDate,typeMismatch.dueDate,typeMismatch.java.util.Calendar,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [bill.dueDate,dueDate]; arguments []; default message [dueDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Calendar' for property 'dueDate'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.util.Calendar] for value '05/25/2017'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [05/25/2017]]
Please, what is the best approach to test the persistence of an entity that has a field of Calendar type using jUnit, spring and MockHttpServletRequestBuilder ? Is there another way to do this and achieve the same goal ? I appreciate any help. Thanks.

Related

Problem with Kotlin inline class deserialization to JSON in tests

I'm trying to write tests for routes in a WebFlux application, but I encountered a problem. I got an error during parsing to JSON:
13:38:20.601 [parallel-1] DEBUG org.springframework.web.server.handler.ResponseStatusExceptionHandler - [248c6c90] Resolved [ServerWebInputException: "400 BAD_REQUEST "Failed to read HTTP message"; nested exception is org.springframework.core.codec.DecodingException: JSON decoding error: Cannot construct instance of `com.test.GetItemRequest` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.test.GetItemRequest` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)<EOL> at [Source: (org.springframework.core.io.buffer.DefaultDataBuffer$DefaultDataBufferInputStream); line: 1, column: 2]"] for HTTP POST /v1/download
I have a request that uses an inline class:
#JvmInline
value class ItemName(val rawValue: String)
data class GetItemRequest(val name: ItemName)
The test:
#Test
fun `Request is parsed successfully`() {
//...
val client = WebTestClient.bindToRouterFunction(router.eimApiRoutes()).build()
val request = """{"name":"item1"}"""
val resp = client
.post()
.uri(EimApiRouter.DOWNLOAD_PATH)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(Mono.just(request), String::class.java)
.exchange()
.expectStatus()
.isOk
.expectBody()
.returnResult()
.responseBody
val expectedResponse = "OK"
assertEquals(expectedResponse, String(resp!!))
}
I can work the problem around by adding a default constructor:
data class GetItemRequest(val name: ItemName) {
constructor() : this(ItemName(""))
}
When I change the type of the parameter to String, it works.
The question:
Could somebody give the real solution? Maybe there is something missing in the test web client configuration (I tried to configure the Kotlin codecs manually, but without success)?

spring tests with mocks and HttpMessageConversionException

i have something like that in my ParkingServiceController.
#PostMapping("/departure")
public ResponseEntity<String> departure(#RequestBody CarAtGateModel carAtGateModel) throws UnidentifiedCarException {
CarAndParkingEntity carAndParkingEntity = carsAndParkingsRepository.findByIdCar(
carAtGateModel.getCarEntity().getIdCar()).orElseThrow(() -> new UnidentifiedCarException());
carAndParkingEntity.setIdParking("-1");
carsAndParkingsRepository.flush();
return new ResponseEntity<>(responsesMessages.gateUp(), HttpStatus.OK);
}
and next i wanted to do test with some mocks.
#Test
public void testArrivalWhenParkingIdNotExists() {
//given
CarAndParkingEntity carAndParkingEntity = mock(CarAndParkingEntity.class);
carAtGateModel = mock(CarAtGateModel.class);
//when
when(carsAndParkingsRepository.findByIdCar(anyString())).thenReturn(Optional.of(carAndParkingEntity));
HttpEntity<CarAtGateModel> request = new HttpEntity<>(carAtGateModel);
ResponseEntity response = testRestTemplate.postForEntity("/departure", request, String.class);
//then
assertEquals("Parking with that id does not exists", response.getBody());
}
but i'm getting that exception every time with every code change in test
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.mockito.internal.creation.bytebuddy.ByteBuddyCrossClassLoaderSerializationSupport and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.example.parkingservice.models.CarAtGateModel$MockitoMock$1316802841["mockitoInterceptor"]->org.mockito.internal.creation.bytebuddy.MockMethodInterceptor["serializationSupport"])
When i'm not using any mock test passess correctly so imo i'm doing something wrong with mocking
At first sight, as your ParkingServiceController has a composition with CarsAndParkingRepository, you should mock that dependency first. I don't know why you are mocking carAtGateModel, you can use a real object representing the the data you want to pass to the controller (the same applies to carAndParkingEntity).
It would be helpful if you add more details, explaining what you want to test because it's not totally clear the assertion you are doing.

How to change WebClient behaviour concerning LocalDateTime?

I am using Spring Boot 2.4.5 and try to request a REST API of another application using WebClient. I know that the other application provides the requested information as a Collection.
When I use an Object[] to receive the response:
Object[] ob = (Object[]) webClient
.get()
.uri(endpoint)
// .bodyValue(criteria)
.exchangeToMono(response -> {
if (response.statusCode()
.equals(HttpStatus.OK)) {
return response.bodyToMono(Object[].class);
} else if (response.statusCode()
.is4xxClientError()) {
return Mono.just("Error response");
} else {
return response.createException()
.flatMap(Mono::error);
}
}).block();
I can see that I receive a LinkedHashMap with all the values including a field:
date_of_declaration -> 2020-03-02T08:43:10
However, if possible, I want to let WebClient immediately convert the response into the designated DTOs...
DeclarationDTO[] ob = (DeclarationDTO[]) webClient
.get()
.uri(endpoint)
// .bodyValue(criteria)
.exchangeToMono(response -> {
if (response.statusCode()
.equals(HttpStatus.OK)) {
return response.bodyToMono(DeclarationDTO[].class);
} else if (response.statusCode()
.is4xxClientError()) {
return Mono.just("Error response");
} else {
return response.createException()
.flatMap(Mono::error);
}
}).block();
...I get an exception when a LocalDateTime object shall be deserialized.
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize value of type `java.time.LocalDateTime` from String "02-03-2020 01:20:00": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '02-03-2020 01:20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {HourOfAmPm=1, NanoOfSecond=0, SecondOfMinute=0, MicroOfSecond=0, MinuteOfHour=20, MilliOfSecond=0},ISO resolved to 2020-03-02 of type java.time.format.Parsed; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "02-03-2020 01:20:00": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '02-03-2020 01:20:00' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {HourOfAmPm=1, NanoOfSecond=0, SecondOfMinute=0, MicroOfSecond=0, MinuteOfHour=20, MilliOfSecond=0},ISO resolved to 2020-03-02 of type java.time.format.Parsed
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1438] (through reference chain: java.lang.Object[][0]->de.xxx.myportal.api.infrastructure.dto.MyDTO["a_person"]->de.xxx.myportal.api.infrastructure.dto.APersonDTO["foobar"])
I think WebClient has an internal ObjectMapper, so maybe it is possible to modify this ObjectMapper during WebClient instantiation? ...or is there a better way to tell Webclient how to handle LocalDateTime? ...maybe a WebClient customized by a configuration or...?
I can't explain but after removing any JsonFormat annotation it works out of the box. (Weird!)
This may help https://www.baeldung.com/spring-boot-formatting-json-dates
In your DeclarationDTO you can use something like:
#JsonFormat(pattern="yyyy-MM-dd'T'HH:mm:ss")
#JsonProperty("date_of_declaration")
private LocalDateTime dateOfDeclaration;

How to check Bad request for #Min on request param in spring controller?

I am pretty new to spring controller. I am trying to write unit test for invalid parameter. I have an api that has #RequestParam("id") #Min(1) long id and in my unit test, I pass in "-1". Here is my test:
#Test
public void searchWithInvalidIbId() throws Exception {
mockMvc.perform(get(BASE_URL)
.param(COLUMN_IB_ID, INVALID_IB_ID_VALUE) // = "-1"
.param(COLUMN_TIME_RANGE, TIME_RANGE_VALUE)
.param(COLUMN_TIME_ZONE, TIME_ZONE_VALUE)
.accept(PowerShareMediaType.PSH_DISPATCH_REPORTER_V1_JSON)
.contentType(PowerShareMediaType.PSH_DISPATCH_REPORTER_V1_JSON))
.andExpect(status().isBadRequest());
}
When I run this, I get
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.ConstraintViolationException: search.arg2: must be greater than or equal to 1
It makes sense, but I am not sure how to test this is BadRequest. I tried #Test(expected = NestedServletException.class), and it passed, but I don't think it is checking what I want to check. What is the right approach to check this?
You can have your custom exception handler annotated with #ControllerAdvice and handle ConstraintViolationException in that class. You can throw your custom exception with additional details if you wish.
Here is an example approach:
#ControllerAdvice
public class MyCustomExceptionHandler {
#ResponseBody
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(ConstraintViolationException.class)
ApiError constraintViolationException(ConstraintViolationException e) {
return BAD_REQUEST.apply(e.getBindingResult());
}
}
Here ApiError is a custom class to represent your error response, it can be anything else you want. You can add timestamp, http status, your error message etc.

spring mvc controller Test with Enumeration value

i'm trying to test this Method :
#RequestMapping(value="/PersonalState/{EmployeeId}", method = RequestMethod.PUT)
public #ResponseBody Object Update(#PathVariable Integer EmployeeId, #RequestParam EmployeeState empstate) throws Exception {
EmployeeService.updateEmployeeState(entityManager.find(Employee.class, EmployeeId), empstate);
return null;
}
EmplyeeState is an enumeration , the values are saved in db as integer,this is my test Code:
#Test
public void EmployeeTest() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.put("/PersonalState/{empstate}",EmplyeeState.PERMANENT)
.param("EmployeeId", "550"))
.andDo(print())
.andExpect(MockMvcResultMatchers.status().isOk());
}
I got this Errror:
Resolved Exception:
Type = org.springframework.beans.TypeMismatchException
MockHttpServletResponse:
Status = 400
I tried to pass the two variables as parameters ,passing only the EmployeeId as parameter but i still have the same error besides the param parameters must be both of type String.
Any Idea?
Problem resolved.
i passed as parameter the enum string value.

Resources