如何测试 MapStruct 映射器实现并模拟其依赖项
how to test MapStruct mapper implementation and mock its dependencies
我正在使用 MapStruct 将对象从 DTO 映射到 DTO。例如,当从具有 ID 列表的 DTo 映射到具有其他 POJO 列表的 POJO 时,我的映射器依赖于某些 services/repositories 从数据库中获取数据。
为此,我有一个 Mapper 接口和一个实现此接口的抽象 class 装饰器。
我想测试映射器,但我需要模拟装饰器内的服务。我的问题是我怎样才能做到这一点?
现在我知道如果 mapper 没有那么多依赖关系会更好 (SOLID),但我现在需要快速完成项目。
看起来像这样:
映射器
@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);
}
装饰器
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;
}
}
然后是生成的实现:
@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 );
}
}
好的,我在提出问题几分钟后找到了答案。好讽刺
如果有人需要,我会post这里。
您只需将 Setters 添加到 services/repositories 的装饰器,并测试设置了 mocks 代替依赖项的实现。
像这样:
装饰者:
@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;
}
}
测试:
@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());
}
}
我正在使用 MapStruct 将对象从 DTO 映射到 DTO。例如,当从具有 ID 列表的 DTo 映射到具有其他 POJO 列表的 POJO 时,我的映射器依赖于某些 services/repositories 从数据库中获取数据。 为此,我有一个 Mapper 接口和一个实现此接口的抽象 class 装饰器。 我想测试映射器,但我需要模拟装饰器内的服务。我的问题是我怎样才能做到这一点?
现在我知道如果 mapper 没有那么多依赖关系会更好 (SOLID),但我现在需要快速完成项目。
看起来像这样:
映射器
@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);
}
装饰器
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;
}
}
然后是生成的实现:
@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 );
}
}
好的,我在提出问题几分钟后找到了答案。好讽刺
如果有人需要,我会post这里。
您只需将 Setters 添加到 services/repositories 的装饰器,并测试设置了 mocks 代替依赖项的实现。
像这样:
装饰者:
@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;
}
}
测试:
@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());
}
}