I am big on clean well-isolated unit tests. But I am stumbling on the \"clean\" part here for testings a controller that uses DomainClassConverter
feature to get en
So from the DomainClassConverter small documentation I know that it uses CrudRepository#findById to find entities. What I would like to know is how can I mock that cleanly in a test.
You will need to mock 2 methods that are called prior the CrudRepository#findById
in order to return the entity you want. The example below is using RestAssuredMockMvc
, but you can do the same thing with MockMvc if you inject the WebApplicationContext
as well.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SomeApplication.class)
public class SomeControllerTest {
@Autowired
private WebApplicationContext context;
@MockBean(name = "mvcConversionService")
private WebConversionService webConversionService;
@Before
public void setup() {
RestAssuredMockMvc.webAppContextSetup(context);
SomeEntity someEntity = new SomeEntity();
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
}
}
At some point Spring Boot will execute the WebConversionService::convert
, which will later call DomainClassConverter::convert
and then something like invoker.invokeFindById
, which will use the entity repository to find the entity.
So why mock WebConversionService
instead of DomainClassConverter
? Because DomainClassConverter
is instantiated during application startup without injection:
DomainClassConverter<FormattingConversionService> converter =
new DomainClassConverter<>(conversionService);
Meanwhile, WebConversionService
is a bean which will allow us to mock it:
@Bean
@Override
public FormattingConversionService mvcConversionService() {
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat());
addFormatters(conversionService);
return conversionService;
}
It is important to name the mock bean as mvcConversionService
, otherwise it won't replace the original bean.
Regarding the stubs, you will need to mock 2 methods. First you must tell that your mock can convert anything:
when(webConversionService.canConvert(any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(true);
And then the main method, which will match the desired entity ID defined in the URL path:
when(webConversionService.convert(eq("1"), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(someEntity);
So far so good. But wouldn't be better to match the destination type as well? Something like eq(TypeDescriptor.valueOf(SomeEntity.class))
? It would, but this creates a new instance of a TypeDescriptor, which will not match when this stub is called during the domain conversion.
This was the cleanest solution I've put to work, but I know that it could be a lot better if Spring would allow it.