i使用mapstruct映射我的实体,我正在使用Mockito嘲笑我的对象。
我想测试一种包含带有映射的映射的方法。问题是嵌套映射器在我的单元测试中始终为无效(在应用程序中效果很好)
这是我的映射器声明:
@Mapper(componentModel = "spring", uses = MappingUtils.class)
public interface MappingDef {
UserDto userToUserDto(User user)
}
这是我的嵌套映射器
@Mapper(componentModel = "spring")
public interface MappingUtils {
//.... other mapping methods used by userToUserDto
这是我要测试的方法:
@Service
public class SomeClass{
@Autowired
private MappingDef mappingDef;
public UserDto myMethodToTest(){
// doing some business logic here returning a user
// User user = Some Business Logic
return mappingDef.userToUserDto(user)
}
这是我的单位测试:
@RunWith(MockitoJUnitRunner.class)
public class NoteServiceTest {
@InjectMocks
private SomeClass someClass;
@Spy
MappingDef mappingDef = Mappers.getMapper(MappingDef.class);
@Spy
MappingUtils mappingUtils = Mappers.getMapper(MappingUtils.class);
//initMocks is omitted for brevity
@test
public void someTest(){
UserDto userDto = someClass.myMethodToTest();
//and here some asserts
}
mappingDef
正确注入,但 mappingUtils
始终为null
Disclamer :这不是此问题的重复。他正在使用@Autowire,因此他正在加载春季上下文,因此他正在进行集成测试。我正在进行单元测试,所以我不使用@Autowired
我不想制作mappingDef
和mappingUtils
@Mock
,因此我不需要在每个用例中进行when(mappingDef.userToUserDto(user)).thenReturn(userDto)
如果您愿意使用春季测试util,则org.springframework.test.util.ReflectionTestUtils
相当容易。
MappingDef mappingDef = Mappers.getMapper(MappingDef.class);
MappingUtils mappingUtils = Mappers.getMapper(MappingUtils.class);
...
// Somewhere appropriate
@Before
void before() {
ReflectionTestUtils.setField(
mappingDef,
"mappingUtils",
mappingUtils
)
}
强制映射以构造函数注入生成实现
@Mapper(componentModel = "spring", uses = MappingUtils.class, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface MappingDef {
UserDto userToUserDto(User user)
}
@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface MappingUtils {
//.... other mapping methods used by userToUserDto
使用构造函数注入,以便您可以使用映射器构建正在测试的类。
@Service
public class SomeClass{
private final MappingDef mappingDef;
@Autowired
public SomeClass(MappingDef mappingDef) {
this.mappingDef = mappingDef;
}
public UserDto myMethodToTest(){
// doing some business logic here returning a user
// User user = Some Business Logic
return mappingDef.userToUserDto(user)
}
测试躯体。注意:它不是您在这里测试的映射器,因此可以嘲笑映射器。
@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest {
private SomeClass classUnderTest;
@Mock
private MappingDef mappingDef;
@Before init() {
classUnderTest = new SomeClass(mappingDef);
// defaultMockBehaviour:
when(mappingDef.userToUserDto(anyObject(User.class).thenReturn(new UserDto());
}
@test
public void someTest(){
UserDto userDto = someClass.myMethodToTest();
//and here some asserts
}
在真实的单元测试中,也测试映射器。
@RunWith(MockitoJUnitRunner.class)
public class MappingDefTest {
MappingDef classUnderTest;
@Before
void before() {
// use some reflection to get an implementation
Class aClass = Class.forName( MappingDefImpl.class.getCanonicalName() );
Constructor constructor =
aClass.getConstructor(new Class[]{MappingUtils.class});
classUnderTest = (MappingDef)constructor.newInstance( Mappers.getMapper( MappingUtils.class ));
}
@Test
void test() {
// test all your mappings (null's in source, etc)..
}
无需使用反射。对我来说,最简单的方法是以下内容:
@RunWith(MockitoJUnitRunner.class)
public class NoteServiceTest {
@InjectMocks
private SomeClass someClass;
@Spy
@InjectMocks
MappingDef mappingDef = Mappers.getMapper(MappingDef.class);
@Spy
MappingUtils mappingUtils = Mappers.getMapper(MappingUtils.class);
但是,这仅在嵌套映射器的第一个级别上起作用。如果您使用使用映射器的映射器的映射器,则使用thrid映射器要比使用 ReflectionTestUtils
将第三个映射器注入第二个映射器。
作为SJAAK答案的变体,现在可以依靠映射本身来检索实现类,同时还可以通过正确使用grensics来避免铸造:
Class<? extends MappingDef> mapperClass = Mappers.getMapperClass(MappingDef.class);
Constructor<? extends MappingDef> constructor = mapperClass.getConstructor(MappingUtils.class);
MappingDef mappingDef = constructor.newInstance(Mappers.getMapper(MappingUtils.class));
这甚至可以通过检查构造函数来完全通用,找到所有映射器所需的参数并递归解决这些映射器。
所以,尝试以下操作:
maven:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>test</scope>
</dependency>
@ComponentScan(basePackageClasses = NoteServiceTest.class)
@Configuration
public class NoteServiceTest {
@Autowired
private SomeClass someClass;
private ConfigurableApplicationContext context;
@Before
public void springUp() {
context = new AnnotationConfigApplicationContext( getClass() );
context.getAutowireCapableBeanFactory().autowireBean( this );
}
@After
public void springDown() {
if ( context != null ) {
context.close();
}
}
@test
public void someTest(){
UserDto userDto = someClass.myMethodToTest();
//and here some asserts
}
最好是一直使用构造函数注入...也在SomeClass
中,并使用@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
..然后,您不需要在测试用例中嘲笑春季/春季。
如前所述,您可以使用其他映射器将 injectionStrategy = InjectionStrategy.CONSTRUCTOR
用于映射器(在这种情况下为MappingDef
)。
,而不是在测试中:
@Spy
MappingUtils mappingUtils = Mappers.getMapper(MappingUtils.class);
@Spy
MappingDef mappingDef = new MappingDefImpl(mappingUtils);
也许不是最优雅的,但它起作用。