地图:嘲笑嵌套映射器



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

我不想制作mappingDefmappingUtils @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);

也许不是最优雅的,但它起作用。

相关内容

  • 没有找到相关文章

最新更新