How to Test Mapstruct Mapper Implementation and Mock Its Dependencies

I am using MapStruct to map objects from and to DTOs. My mappers are dependent on some services/repositories to get data from database when for example mapping from DTo that has list of IDs to POJO that has a list of other POJOs. For this I have a Mapper inteface and an abstract class Decorator implementing this interface. I want to test the mapper but I need to mock the services inside the decorator. My question is how can I achieve this?

Now I know there would be better if mapper didn't have so much dependencies (SOLID), but I need to finish the project fast now.

It looks like this:

Mapper

@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
@DecoratedWith(AuthorMapperDecorator.class)
public interface AuthorMapper {

    AuthorDTO map(Author entity);

    @Mapping(target = "songs", ignore = true)
    Author map(AuthorDTO dto);

    @Mapping(target = "coauthorSongs", ignore = true)
    @Mapping(target = "songs", ignore = true)
    @Mapping(target = "id", ignore = true)
    @Mapping(target = "name", source = "name")
    Author map(UniversalCreateDTO dto);
}

Decorator

public abstract class AuthorMapperDecorator implements AuthorMapper {

  @Autowired
  @Qualifier("delegate")
  private AuthorMapper delegate;
  @Autowired
  private SongCoauthorService songCoauthorService;
  @Autowired
  private SongService songService;

  @Override
  public Author map(AuthorDTO dto) {
    var author = delegate.map(dto);
    author.setBiographyUrl(null);
    author.setPhotoResource(null);
    author.setCoauthorSongs(new HashSet<>(songCoauthorService.findByAuthorId(dto.getId())));
    author.setSongs(new HashSet<>(songService.findByAuthorId(dto.getId())));
    return author;
  }

  @Override
  public Author map(UniversalCreateDTO dto) {
    var author = delegate.map(dto);
    author.setSongs(new HashSet<>());
    author.setCoauthorSongs(new HashSet<>());
    author.setId(Constants.DEFAULT_ID);
    return author;
  }
}

Then there are the generated implementations:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-05-13T22:50:36+0200",
    comments = "version: 1.3.1.Final, compiler: javac, environment: Java 13.0.2 (Oracle Corporation)"
)
@Component
@Qualifier("delegate")
public class AuthorMapperImpl_ implements AuthorMapper {

    @Override
    public AuthorDTO map(Author entity) {
        if ( entity == null ) {
            return null;
        }

        Builder authorDTO = AuthorDTO.builder();

        authorDTO.id( entity.getId() );
        authorDTO.name( entity.getName() );

        return authorDTO.build();
    }

    @Override
    public Author map(AuthorDTO dto) {
        if ( dto == null ) {
            return null;
        }

        Author author = new Author();

        author.setId( dto.getId() );
        author.setName( dto.getName() );

        return author;
    }

    @Override
    public Author map(UniversalCreateDTO dto) {
        if ( dto == null ) {
            return null;
        }

        Author author = new Author();

        author.setName( dto.getName() );

        return author;
    }
}
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-05-13T22:50:36+0200",
    comments = "version: 1.3.1.Final, compiler: javac, environment: Java 13.0.2 (Oracle Corporation)"
)
@Component
@Primary
public class AuthorMapperImpl extends AuthorMapperDecorator implements AuthorMapper {

    private final AuthorMapper delegate;

    @Autowired
    public AuthorMapperImpl(@Qualifier("delegate") AuthorMapper delegate) {

        this.delegate = delegate;
    }

    @Override
    public AuthorDTO map(Author entity)  {
        return delegate.map( entity );
    }
}

1 Answer

Okay, I found an answer minutes after asking thr question. So ironic.

I am going to post it here if somebody needs it.

You just need to add Setters to the decorator for the services/repositories and test the implementation that has the mocks set in place of the dependencies.

Like this:

Decorator:

@Setter
public abstract class AuthorMapperDecorator implements AuthorMapper {

  @Autowired
  @Qualifier("delegate")
  private AuthorMapper delegate;
  @Autowired
  private SongCoauthorService songCoauthorService;
  @Autowired
  private SongService songService;

  @Override
  public Author map(AuthorDTO dto) {
    var author = delegate.map(dto);
    author.setBiographyUrl(null);
    author.setPhotoResource(null);
    author.setCoauthorSongs(new HashSet<>(songCoauthorService.findByAuthorId(dto.getId())));
    author.setSongs(new HashSet<>(songService.findByAuthorId(dto.getId())));
    return author;
  }

  @Override
  public Author map(UniversalCreateDTO dto) {
    var author = delegate.map(dto);
    author.setSongs(new HashSet<>());
    author.setCoauthorSongs(new HashSet<>());
    author.setId(Constants.DEFAULT_ID);
    return author;
  }
}

Test:

@ExtendWith(MockitoExtension.class)
@SpringBootTest(classes = { StkSongbookApplication.class, AuthorMapperImpl.class })
class AuthorMapperTest {

  @Mock
  private SongService songService;

  @Mock
  private SongCoauthorService songCoauthorService;

  @Autowired
  private AuthorMapperImpl impl;

  private AuthorMapper mapper;

  @BeforeEach
  void setUp() {
    impl.setSongCoauthorService(songCoauthorService);
    impl.setSongService(songService);
    mapper = impl;
  }

  @Test
  void testMapToDTO() {
    Author author = new Author();
    author.setId(1L);
    author.setName("dummy name");
    author.setSongs(new HashSet<>());
    author.setCoauthorSongs(new HashSet<>());

    AuthorDTO dto = mapper.map(author);

    assertEquals(author.getName(), dto.getName());
    assertEquals(author.getId(), dto.getId());
  }

  @Test
  void testMapToEntity() {
    AuthorDTO author = AuthorDTO.builder().id(1L).name("dummy name").build();

    Song song1 = new Song();
    song1.setId(1L);
    song1.setTitle("title song1");

    given(songService.findByAuthorId(1L)).willReturn(List.of(new Song[]{song1}));

    Author mapped = mapper.map(author);

    assertEquals(author.getId(), mapped.getId());
    assertEquals(author.getName(), mapped.getName());
    assertEquals(1, mapped.getSongs().size());
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

David Miller

David Miller

Executive Financial & Market Analyst

David Miller brings 15 years of experience in global economics, personal finance strategy, and market dynamics. He specializes in turning complex economic trends into actionable insights for everyday readers.

Share this article
Twitter Facebook Pinterest