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