0

I have a StudentDetailMapper:

@Mapper(componentModel = "spring", uses = SpouseDetailMapper.class)
public interface StudentDetailMapper {
    StudentDetailMapper INSTANCE = Mappers.getMapper(StudentDetailMapper.class);-
    StudentDetailDTO toDTO(StudentDetail studentDetail);
    @InheritInverseConfiguration
    StudentDetail fromDTO(StudentDetailDTO studentDetailDTO);}

I want to write a test for StudentDetailService:

@ExtendWith(MockitoExtension.class)
class StudentDetailServiceTest {
    @Mock
    private StudentDetailRepository studentDetailRepository;

// ???
//    @InjectMocks
//    private SpouseDetailMapper spouseDetailMapper = Mappers.getMapper(SpouseDetailMapper.class);

    @Spy
    private StudentDetailMapper studentDetailMapper = Mappers.getMapper(StudentDetailMapper.class);
    @InjectMocks
    private StudentDetailService studentDetailService;

    @Test
    void getStudentDetail() {
        when(studentDetailRepository.findByStudentStudentID(anyString())).thenReturn(Optional.of(studentDetail));
//      when(spouseDetailMapper.toDTO(spouseDetail)).thenReturn(spouseDetailDTO);
        when(studentDetailMapper.toDTO(studentDetail)).thenReturn(studentDetailDTO);

        StudentDetailDTO result = studentDetailService.getStudentDetail(studentWithDetailID);

        assertThat(studentDetailDTO).isEqualTo(result);
    }

I know that you can write test that has MapStruct with @Spy but if there's a submapper it will throw this error:

java.lang.NullPointerException: Cannot invoke "com.ex.project.mappers.SpouseDetailMapper.toDTO(com.ex.project.model.SpouseDetail)" because "this.spouseDetailMapper" is null

  • Does this answer your question? [MapStruct : mocking nested mapper](https://stackoverflow.com/questions/54774679/mapstruct-mocking-nested-mapper) – GJohannes Jun 18 '23 at 08:54
  • None of it works, this [@Spy @InjectMocks solution](https://stackoverflow.com/a/55009482/17269768) output: `org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class .mappers.StudentDetailMapperImpl`. Or Reflection way output: `org.mockito.exceptions.misusing.MissingMethodInvocationException: when() requires an argument which has to be 'a method call on a mock'`. – vunhatchuong Jun 18 '23 at 11:06
  • Can you show both ways your tried and not just your Exception? For me both solutions work when i recreate those. – GJohannes Jun 18 '23 at 20:12
  • So I just edit my question? I mean I just add those lines into test code above, nothing really special. – vunhatchuong Jun 18 '23 at 22:57
  • Actually reverse the direction of [@Spy @Inject](https://stackoverflow.com/a/74940805/17269768) works for some reason. – vunhatchuong Jun 18 '23 at 23:18

1 Answers1

0

Take a look at this post of the same question, there are quite a few answers, but what works for me is reversing this and add ReflectionTestUtils solution:

My StudentMapper uses SpouseDetailMapper

@Mapper(componentModel = "spring", uses = SpouseDetailMapper.class)
public interface StudentDetailMapper {
    StudentDetailMapper INSTANCE = Mappers.getMapper(StudentDetailMapper.class);-
    StudentDetailDTO toDTO(StudentDetail studentDetail);
    @InheritInverseConfiguration
    StudentDetail fromDTO(StudentDetailDTO studentDetailDTO);
}
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface SpouseDetailMapper {
    SpouseDetailMapper INSTANCE = Mappers.getMapper(SpouseDetailMapper.class);
    @Mapping(target = "studentDetail", ignore = true)
    SpouseDetailDTO toDTO(SpouseDetail spouseDetail);
    @InheritInverseConfiguration
    SpouseDetail fromDTO(SpouseDetailDTO spouseDetailDTO);
}

Then in Test:

@ExtendWith(MockitoExtension.class)
class StudentDetailServiceTest {
    @Spy
    StudentDetailMapper studentDetailMapper = Mappers.getMapper(StudentDetailMapper.class);
    @Spy
    @InjectMocks
    SpouseDetailMapper spouseDetailMapper = Mappers.getMapper(SpouseDetailMapper.class);
    @Mock
    private StudentDetailRepository studentDetailRepository;
    @InjectMocks
    private StudentDetailService studentDetailService;
    @BeforeEach
    void setUp() {
        ReflectionTestUtils.setField(
            studentDetailMapper,
            "spouseDetailMapper",
            spouseDetailMapper);
}

So what you want to do is add @Spy @InjectMocks into the used class, in this case it's SpouseDetail.