JUnit的 - 如何嘲笑MapStruct嵌套映射(JUnit - How to mock Map

2019-10-29 14:12发布

当我运行一个服务层类单元测试我收到一个恼人的NPE。 这个类使用来自MapStruct,其内部使用其他映射器自动生成的映射器(参见映射器注释的“使用”属性):

    Mapper(componentModel = "spring", uses = {UserMapper.class})
public interface CandidateMapper extends EntityMapper<CandidateDTO, Candidate> {



  @Mapping(target = "createdBy", ignore = true)
  @Mapping(target = "createdDate", ignore = true)
  @Mapping(target = "lastModifiedBy", ignore = true)
  @Mapping(target = "lastModifiedDate", ignore = true)
  @Mapping(target = "applications", ignore = true)
  Candidate toEntity(CandidateDTO candidateDTO);

  default Candidate fromId(Long id) {
    if (id == null) {
      return null;
    }
    Candidate candidate = new Candidate();
    candidate.setId(id);
    return candidate;
  }
}

我的单元测试是:

    @RunWith(SpringRunner.class)
    public class CandidateServiceTest {

      private CandidateService candidateService;

      @MockBean
      private UserRepository userRepository;
      @MockBean
      CandidateRepository candidateRepository;
      @MockBean
      UserDetailsService userDetailsService;

      CandidateMapper candidateMapper = Mappers.getMapper(CandidateMapper.class);

      UserMapper userMapper = Mappers.getMapper(UserMapper.class);

      @Before
      public void init() {
      this.candidateService = new CandidateService(candidateRepository,
      candidateMapper, userDetailsService, userMapper);
      }
      @Test
      @WithMockUser(authorities = RolesConstants.ADMIN)
      public void givenUser_whenGetCandidateOrCreateForLogin_create() {
        // Pre-conditions
       ...

        // Mocking data
       ...

        // Given
        given(userRepository.findOneByLogin(eq(login)))
          .willReturn(Optional.of(user));
        given(candidateRepository.findOneByUserLogin(eq(login)))
          .willReturn(Option.of(candidate));

        // When
        CandidateDTO candidateDTO = candidateService.getCandidateOrCreateForLogin(login);

        // Then
       ...
      }

该NPE由该行提出:

candidateDTO.setUser( userMapper.toDto( candidate.getUser() ) );

在CandidateMapperImpl因为candidateMapperImpl内userMapperImpl实例(变量名userMapper)为空。

当我启动与春天启动的应用程序不会出现这种情况:运行,但只与单元测试

任何意见或建议,将不胜感激。 让我知道如果你需要更多的相关信息或细节,或者如果我错过了一些重要的东西。

谢谢

编辑:

我固定的注释与@Autowired的映射和使用这个类注释的问题:

@SpringBootTest(classes = {CandidateMapperImpl.class, UserMapperImpl.class, UserRolesMapperImpl.class,
  CandidateMapper.class, UserMapper.class, UserRolesMapper.class})

要概括:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {SubMapperImpl.class, SubMapper.class, 
OtherSubMapper.class, OtherSubMapperImpl.class...})
public class ServiceTest {

@Autowired
Mapper mapper;

...

}

Answer 1:

看来,根据您的测试带注释@RunWith(SpringRunner.class)这是加入Spring上下文来测试),你真的不希望成为你的嘲讽嵌套映射器,是否正确? 如果你想Spring上下文,只是自动装配及其所有嵌套的映射器映射为好。 请参阅: https://stackoverflow.com/a/48503708/1098564

下面(与...的Mockito不熟悉MockBean还),会给你一个“固定”(即CandidateService)与注入(通过@Autowired)所有嵌套映射器和依赖的其余嘲笑了。 有可能与MockBean一个更清洁的方式,但目前并不具备时间来检验这一切。

@RunWith(SpringRunner.class)
@SpringBootTest
public class CandidateServiceTest {

    @Autowired
    @InjectMocks
    private CandidateService fixture;

    @Mock
    private UserRepository userRepository;
    @Mock
    private CandidateRepository candidateRepository;
    @Mock
    private UserDetailsService userDetailsService;
    @Autowired
    private CandidateMapper candidateMapper;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
    }
    //....tests
}


文章来源: JUnit - How to mock MapStruct nested mapper