如何编写单元测试验证MapStruct所有target目标字段均已被正确映射?
验证MapStruct目标字段全映射的实现思路
你可以从以下三个方向实现,按优先级从高到低排序:
1. 优先用MapStruct编译期检查(无需写单元测试)
MapStruct原生支持未映射目标字段的检查,直接在@Mapper注解中添加配置即可:
@Mapper( uses = {DateMapper.class, StateMapper.class}, unmappedTargetPolicy = ReportingPolicy.ERROR ) public abstract class Blah implements IDtoContextMapper<SourceEntity, StuffDto> { // 原有逻辑不变 }
配置后只要StuffDto有字段未配置映射规则,编译阶段就会直接抛出错误,比单元测试更早发现问题,是成本最低的方案。如果你的场景存在部分字段需要后续手动赋值、不需要MapStruct映射的情况,可以给对应字段加@Mapping(target = "xxx", ignore = true)显式忽略即可。
2. 单元测试:构造测试数据+反射校验DTO字段非默认值
如果需要用单元测试兜底校验,最易实现的方案是构造全量赋值的源对象,映射后校验DTO所有字段都不是初始默认值:
@Test void testAllStuffDtoFieldsAreMapped() throws IllegalAccessException { // 1. 构造源实体,所有字段赋值为非默认值(也可以用反射工具类批量赋值,避免手写) SourceEntity source = new SourceEntity(); source.setId(1L); source.setSomeNumber(999); source.setStatus(StatusEnum.ENABLE); // 其他源字段依次赋值 // 2. 执行映射 StuffDto dto = blahMapper.toDto(source, new MappingContext()); // 3. 反射遍历DTO所有字段校验 for (Field field : StuffDto.class.getDeclaredFields()) { // 跳过静态、final字段 if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) { continue; } field.setAccessible(true); Object fieldValue = field.get(dto); // 校验字段值不是默认值 assertNotNull(fieldValue, "字段未被映射:" + field.getName()); // 基本类型额外判断非默认值,比如int不能为0、boolean不能为false等 } }
这个方案的优点是逻辑简单无额外依赖,缺点是需要维护源对象的赋值逻辑,适合字段不多的场景。
3. 字节码校验:扫描MapStruct生成类的setter调用
如果不想构造测试数据,可以用ArchUnit等字节码扫描工具,直接校验MapStruct生成的实现类调用了StuffDto的所有setter方法:
@ArchTest static final ArchRule allDtoSettersAreUsedInMapper = methods().that().areDeclaredIn(StuffDto.class) .and().haveNameStartingWith("set") .should().beCalledByClassesThat().areAssignableTo(Blah.class);
这个方案不需要执行业务映射逻辑,执行速度快,适合字段多、构造测试数据成本高的场景。
内容的提问来源于stack exchange,提问作者DerBenniAusA
相关产品推荐
相关产品推荐

