In one of my projects I let Spring Boot automatically bind domain objects from the database.
This fails in the reduced unit test setup, because DomainClassConverter
is not available. But it can be mocked!
I got some hints on Stack Overflow:
- Unit Test using the DomainClassConverter feature of Spring
- How to cleanly test Spring Controllers that retrieve parameters with DomainClassConverter?
These hints did not help me, because mocking the required method call alone caused issues with other method calls failing.
Then I read about reconfiguring @MockBean
with Answer.CALL_REAL_METHODS
. This did not work either, because the bean was not initialized properly (converterCache
was null).
A @SpyBean
configuration was the solution: @SpyBean
does initialize the bean and it is possible to selected method calls:
@WebMvcTest(controllers = InvoiceItemController.class)
@WithMockUser(roles = {"INVOICE"})
public class InvoiceItemControllerTest extends BaseControllerTest implements CrudControllerTest {
@Autowired
protected MockMvc mockMvc;
@SpyBean(name = "mvcConversionService")
FormattingConversionService webConversionService;
Invoice invoice;
@BeforeEach
public void initMocks() {
invoice = new Invoice();
invoice.setId(3L);
}
@Override
@Test
public void createForm() throws Exception {
// workaround for missing DomainClassConverter in reduced test setup
Mockito.doReturn(true)
.when(webConversionService)
.canConvert(
argThat(argument
-> argument.getObjectType().equals(String.class)),
argThat((ArgumentMatcher<TypeDescriptor>) argument
-> argument.getObjectType().equals(Invoice.class)));
Mockito.doReturn(invoice)
.when(webConversionService)
.convert(eq("3"), any(TypeDescriptor.class), any(TypeDescriptor.class));
getMockMvc().perform(get(getBaseUrl() + "/create").param("invoice", "3"))
.andExpect(status().isOk());
}
}
Using @SpyBean
with WebConversionService
did not work because of a missing default constructor. Therefore I switched to FormattingConversionService
wich is configured in WebMvcAutoConfiguration
as mvcConversionService