带有 MapStruct 的 @SpringBootTest 需要 Impl

@SpringBootTest with MapStruct requires Impl

我有以下测试:

@SpringBootTest(classes = {SomeService.class, DtoMapperImpl.class})
class SomeServiceTest {

以及以下映射器:

@Mapper(componentModel = "spring")
public interface DtoMapper {
    EntityDto toDto(Entity entity);
}

我没有更改包(这意味着 DtoMapperImplDtoMapper 在同一个包中)

一旦我将 Impl 更改为接口,我的测试就失败了:

@SpringBootTest(classes = {SomeService.class, DtoMapper.class})
class SomeServiceTest {

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someService': Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'DtoMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

能否请您建议解决此问题的最佳方法?我在 MapStruct 1.3.1.Final

创建以下配置(应指向映射器所在的位置):

@TestConfiguration
@ComponentScan("some.package.mapper")
public class MappersConfig {
}

并修改切片:

@SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {

问题实际上与 MapStruct 无关,而是与 SpringBootTest#classes 的使用方式有关。

SpringBootTest 中的 classes 旨在提供您应该用于在测试中加载的组件。

来自 JavaDoc:

The component classes to use for loading an ApplicationContext. Can also be specified using @ContextConfiguration(classes=...). If no explicit classes are defined the test will look for nested @Configuration classes, before falling back to a @SpringBootConfiguration search. Returns: the component classes used to load the application context

在你的例子中你有 2 个 classes:

  • SomeService - 我假设是 class 注释 @Service 并且 Spring 将正确加载它
  • DtoMapper - 这是 MapStruct 映射器,它是一个接口,而不是一个组件。您想要用于测试的组件是 DtoMapperImpl

您有多种选择来解决此问题:

使用 Impl class

您可以在 SpringBootTest#classes 中使用 DtoMapperImpl(Spring 组件 class),然后您的测试将加载正确的组件

使用自定义配置 class 将组件扫描您的映射器

@TestConfiguration
@ComponentScan("com.example.mapper")
public class MappersConfig {

}

然后在您的 SpringBootTest#classes 中使用它。例如

@SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {
   ...
}