JUnit - How to mock MapStruct nested mapper

白昼怎懂夜的黑 提交于 2020-01-06 07:36:12

问题


I'm receiving an annoying NPE when I run a Unit Test for a service layer class. This class use an autogenerated mapper from MapStruct, which inside use another mapper (see in the Mapper annotation the "uses" attribute):

    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;
  }
}

My UnitTest is:

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

The NPE is raised by this line:

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

in the CandidateMapperImpl because the userMapperImpl instance (variable name userMapper) inside the candidateMapperImpl is null.

This does not happen when I launch the application with spring-boot:run but only with the UnitTest

Any ideas or suggest would be appreciated. Let me know if you'd need more infos or details or if I missed something important.

Thanks

EDIT:

I fixed the issues annotating the Mapper with @Autowired and using this class annotation:

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

To generalize:

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

@Autowired
Mapper mapper;

...

}

回答1:


It seems, based on your test annotated with @RunWith(SpringRunner.class) (which is for adding spring context to your test) that you don't really want to be mocking your nested mappers, correct? If you want spring context, just autowire your mapper with all its nested mappers as well. See: https://stackoverflow.com/a/48503708/1098564

Below (with mockito...not as familiar with MockBean yet), would give you a 'fixture' (i.e. CandidateService) with all the nested mappers injected (via @Autowired) and the rest of the dependencies are mocked out. There's probably a cleaner way with MockBean but don't currently have time to test it all out.

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


来源:https://stackoverflow.com/questions/53155556/junit-how-to-mock-mapstruct-nested-mapper

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!