MapStruct: насмешливый вложенный картограф - PullRequest
0 голосов
/ 19 февраля 2019

Я использую MapStruct для сопоставления своих объектов, и я высмеиваю свои объекты с помощью Mockito.

Я хочу протестировать метод, который содержит сопоставление с mapStruct.Проблема в том, что вложенный маппер всегда пуст в моих модульных тестах (хорошо работает в приложении)

это мое объявление маппера:

@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 всегда пусто

Отказ от ответственности : это не дубликат этого вопроса .Он использует @Autowire, поэтому он загружает контекст Spring, поэтому он выполняет интеграционные тесты.Я делаю юнит-тесты, поэтому я не использую @ Autowired

Я не хочу делать mappingDef и mappingUtils @Mock, поэтому мне не нужно делать when(mappingDef.userToUserDto(user)).thenReturn(userDto) в каждом случае использования

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

заставляет MapStruct генерировать реализации с внедрением конструктора

@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)
}

TestSomeClass.Примечание: здесь тестируется не тот сопоставитель, поэтому сопоставитель можно смоделировать.

@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).. 
  }


0 голосов
/ 02 марта 2019

Итак, попробуйте это:

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) .. Тогда вам не нужно подмешивать пружину / пружину в ваших тестовых примерах.

...