how to test MapStruct mapper implementation and mock its dependencies

Publish date: 2024-04-18

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

ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJobWpwY2uCdYWOoaawZaSkerWx0q1kppmgqMGzwcKtZKaZoKWys3nIpqelnZ2au7Wt06Kmp2WRo7Fuuc6comahpKh6pbHPnqWdnZ6Ytqa%2F