如何为通过 Spring 注入的 mapstruct 抽象映射器编写 Junit 测试

How to write Junit test for mapstruct abstract mapper injected via Spring

我正在使用 MapStruct,mapstruct-jdk8 版本 1.1.0.Final 并定义我通过 Spring.

注入的抽象 class

我正在研究如何通过 Junit 测试来测试它们? 我基本上有一个主映射器,它将使用 2 个子映射器

@Mapper(componentModel = "spring", uses = {SubMapper1.class, SubMapper2.class})
public abstract class MainMapper {

  @Mapping(target = "field1", qualifiedByName = {"MyMapper2Name", "toEntity"})
  public abstract MyEntity toEntity(MyDto pDto);

  public MyDto fromEntity(MyEntity pEntity) {
     // Specific code, hence why I use Abstract class instead of interface. 
  }
}

我已经尝试了几种方法,但无法正确实例化映射器以对其进行测试。

@RunWith(SpringRunner.class)
public class MainMapperTest {

    private MainMapper service = Mappers.getMapper(MainMapper.class);

    @Test
    public void testToEntity() throws Exception {
.....

java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot find implementation for com.mappers.MainMapper

我也通过@InjectMock 尝试过,但也没有骰子。

Cannot instantiate @InjectMocks field named 'service'. You haven't provided the instance at field declaration so I tried to construct the instance. However, I failed because: the type 'MainMapper is an abstract class.

并通过 Spring @Autowired

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.mappers.MainMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

我猜这可能与注释处理器有关,并且在我启动测试时未生成映射器。 我找到 this class as example.

但是 class AnnotationProcessorTestRunner 在 1.2 之前似乎不可用,该版本还没有最终版本。

所以我的问题是如何编写 Junit 测试来测试我在代码中通过 Spring 注入使用的 mapstruct 抽象 class 映射器。

您有多个问题:

  1. 您应该仅将 Mappers#getMapper(Class) 与默认值 componentModel 一起使用,否则映射器将无法正确实例化。如果您在那里获得 RuntimeException,则表示未生成实施 class。确保您的设置正确
  2. 您需要针对实现 MainMapperImpl 而不是抽象 class.
  3. 进行测试
  4. 如果您想使用 spring bean 进行测试,那么您需要使用正确的 ComponentScan 并确保可以自动装配实现和使用的映射器。

您链接的 class 是一个错误的测试 class,与您的测试用例无关。查看 this 集成测试用例 spring 集成。

AnnotationProcessorTestRunner 是我们测试的一部分,用于测试注解处理器,从一开始就存在。它不是发行版的一部分。

为了回应@Richard Lewan 的评论,我是如何使用 2 个 subMappers

为抽象 class ConfigurationMapper 声明我的测试 class
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ConfigurationMapperImpl.class, SubMapper1Impl.class, SubMapper2Impl.class})
public class ConfigurationMapperTest {

您在 SpringBootTest 注释中使用 Impl 生成的 classes 然后注入要测试的 class:

@Autowired
private ConfigurationMapper configurationMapper;

如果您需要更多信息,请告诉我,但从那里开始就很简单了。 我没有模拟 subMapper,因为一次测试所有映射过程对我来说更好。

假设:

  • 您的 MainMapper 映射器被注入 @Component ConverterUsingMainMapper

您可以使用以下示例:

@RunWith(SpringRunner.class)
@ContextConfiguration
public class ConsentConverterTest {

    @Autowired
    MainMapper MainMapper;

    @Autowired
    ConverterUsingMainMapper converter;

    @Configuration
    public static class Config {

        @Bean
        public ConverterUsingMainMapper converterUsingMainMapper() {
            return new ConverterUsingMainMapper();
        }

        @Bean
        public MainMapper mainMapper() {
            return Mappers.getMapper(MainMapper.class);
        }
    }


    @Test
    public void test1() {
        // ... your test.
    }

}

@TheBakker 的补充:作为 @SpringBootTest 的更轻量级替代方案,如果您不需要整个 SpringBoot 堆栈,您可以使用 @ContextConfiguration。他的示例如下所示:

@ExtendWith(SpringExtension.class) // JUnit 5
@ContextConfiguration(classes = {
            ConfigurationMapperImpl.class,
            SubMapper1Impl.class,
            SubMapper2Impl.class
        })
public class ConfigurationMapperTest {
...

在 JUnit 4 中使用注释 RunWith 而不是 ExtendWith:

@RunWith(SpringRunner.class)       // JUnit 4
...

使用 Mockito:

@Spy
private EntityMapper entityMapper = Mappers.getMapper(MyMapper.class);

并记住在您的 class 测试中注入模拟,例如:

@InjectMocks
private MyClassUnderTest myClassUnderTest

您还可以使用映射器及其协作者手动创建应用程序上下文:

ApplicationContext context = new AnnotationConfigApplicationContext(MainMapperImpl.class, SubMapper1Impl.class, SubMapper2Impl.class);

ApplicationContext context = new AnnotationConfigApplicationContext("package.name.for.your.mappers");

然后从上下文中获取映射器

MainMapper mainMapper = context.getBean(MainMapper.class);

百分百有效!

@SpringBootTest
 public class CustomMapperTest {

@Spy
private CustomMapper mapper = Mappers.getMapper(CustomMapper.class);

@Test
public void SCENARIO_CONVERT_ACTIVITY() {
    ActivityEntity dbObj = mapper.toDto(ActivityDTO);
    Assert.assertNotNull(dbObj);
    Assert.assertEquals("XXXX", dbobj.getId());
}

@Mapper
public abstract Class CustomMapper{
  @Mapping(source="activityDto.fathername",target="surname")
  public abstract ActivityEntity toDto(ActivityDTO activityDto);
}