I have simple code:
I use
id 'org.springframework.boot' version '2.7.1'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
Here is my DTOs:
public class CreditDto {
public final String title;
public final String description;
public final Long userId;
public final DurationDto duration;
public final Double sum;
public final RateType rateType;
public final PeriodType periodType;
@DateTimeFormat(pattern = "yyyy-MM-dd")
public final LocalDate startDate;
public CreditDto(final String title,
final String description,
final Long userId,
final DurationDto duration,
Double sum,
final RateType rateType,
final PeriodType periodType,
final LocalDate startDate) {
this.title = title;
this.description = description;
this.userId = userId;
this.duration = duration;
this.sum = sum;
this.rateType = rateType;
this.periodType = periodType;
this.startDate = startDate;
}
public class DurationDto {
public final int days;
public final int months;
public final int years;
public DurationDto(int days,
int months,
int years) {
this.days = days;
this.months = months;
this.years = years;
}
}
Here is my controller:
@RestController
@RequestMapping(path = "/api/v1/credit/",
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE)
public class CreditController {
private final CreditService creditService;
public CreditController(final CreditService creditService) {
this.creditService = creditService;
}
@PostMapping
public void addCredit(final @Valid @RequestBody CreditDto credit) {
creditService.addCredit(credit);
}
}
I run my application, post json by using Postman and everything works perfectly.
Then I have a mockMvc test:
@ExtendWith(MockitoExtension.class)
public class CreditControllerTest {
@InjectMocks
private CreditController creditController;
@Mock
private CreditService creditService;
private MockMvc mockMvc;
@BeforeEach
public void setup() {
mockMvc = standaloneSetup(creditController)
.build();
}
public void addCredit() throws Exception {
// Given
String content = "{"title": "MonthlyFixedCredit", "description": "My awesome second daily floating credit", "userId": 125, "duration": {"days": 0, "months": 5, "years": 0}, "sum": 10000, "startDate": "2022-09-09", "rateType": "FIXED", "periodType": "MONTHLY"}";
doNothing().when(creditService).addCredit(any(CreditDto.class));
// When
final ResultActions result = mockMvc.perform(post("/api/v1/credit/")
.contentType(MediaType.APPLICATION_JSON)
.content(content));
// Then
result.andExpect(status().isOk());
verify(creditService, times(1)).addCredit(any(CreditDto.class));
verifyNoMoreInteractions(creditService);
}
}
And this test failed with:
Request processing failed; nested exception is
org.springframework.http.converter.HttpMessageConversionException: Type definition
error: [simple type, class org.superbank.credit.dto.CreditDto]; nested exception is
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.superbank.credit.dto.CreditDto` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 2]
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.superbank.credit.dto.CreditDto]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.superbank.credit.dto.CreditDto` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 2]
at app//org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at app//org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at app//javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
.........
Could someone help me to understand why actual application works fine without '@JsonCreator' or default constructor but I need to add it for mockMvc. I believed that mockMvc should work in the same way as actual spring mvc and do not need additional configuration just for unit test.
How can I make mockMvc behave the same way as actual mvc, what configuration I missed?