如何为通过 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 映射器。
您有多个问题:
- 您应该仅将
Mappers#getMapper(Class)
与默认值 componentModel
一起使用,否则映射器将无法正确实例化。如果您在那里获得 RuntimeException
,则表示未生成实施 class。确保您的设置正确
- 您需要针对实现
MainMapperImpl
而不是抽象 class. 进行测试
- 如果您想使用 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);
}
我正在使用 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 映射器。
您有多个问题:
- 您应该仅将
Mappers#getMapper(Class)
与默认值componentModel
一起使用,否则映射器将无法正确实例化。如果您在那里获得RuntimeException
,则表示未生成实施 class。确保您的设置正确 - 您需要针对实现
MainMapperImpl
而不是抽象 class. 进行测试
- 如果您想使用 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);
}