如何在嵌套MapStruct Mapper中Mock Service A?
MapStruct Mapper测试中Mock依赖不生效的解决方法
问题场景
测试AnnouncementMapper时,该Mapper通过MapStruct的@Mapper(uses = ForwardMapper.class)引入了ForwardMapper,而ForwardMapper内部依赖ServiceA。尝试MockServiceA的isEnabled方法返回true,但发现ForwardMapper中的ServiceA并非Mock实例,导致Mock逻辑完全不生效。
相关代码
原AnnouncementMapper代码
@Mapper(uses = { ForwardMapper.class // 内部依赖ServiceA }, builder = @Builder(disableBuilder = true), componentModel = "spring" ) public interface AnnouncementMapper { @Mapping(source = ".", target = "carrier") // 其他映射方法... @Mapping(source = ".", target = "forward", qualifiedByName = "ForwardMapper") Schedule mapForSchedule(Announcement announcement); }
原测试代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @AutoConfigureObservability(tracing = false) class Mapper extends Specification { private AService serviceA = Mock() @Autowired Mapper mapper def "map announcement to some other announcement"() { given: 1 * ServiceA.isEnabled(Value.A) >> true when: def result = mapper.map(announcement) // 后续逻辑... } }
注:ForwardMapper是注入了ServiceA的Spring Bean。
问题原因
原测试中仅手动创建了ServiceA的Mock实例,但该实例并未被Spring容器管理,也未注入到ForwardMapper中。ForwardMapper仍使用Spring上下文初始化的原生ServiceA实例,因此Mock的交互逻辑无法触发。
解决方案
方案一:使用@MockBean替换Spring容器中的ServiceA
通过Spring Boot的@MockBean注解,将Mock的ServiceA实例替换Spring容器中原有的实例,所有依赖ServiceA的Bean(包括ForwardMapper)都会自动注入该Mock实例。
修改后的测试代码:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @AutoConfigureObservability(tracing = false) class AnnouncementMapperSpec extends Specification { @MockBean AService serviceA @Autowired AnnouncementMapper mapper def "map announcement to schedule"() { given: 1 * serviceA.isEnabled(Value.A) >> true // 初始化测试用的Announcement对象 def announcement = new Announcement() // 设置announcement的必要属性... when: Schedule result = mapper.mapForSchedule(announcement) then: // 验证forward字段的映射结果符合预期 result.getForward() != null // 其他验证逻辑... } }
方案二:手动将Mock的ServiceA注入到ForwardMapper
如果不想使用@MockBean(比如希望减少Spring上下文加载开销),可以手动获取ForwardMapper实例,并将Mock的ServiceA注入进去。需要确保ForwardMapper提供了ServiceA的setter方法,或通过反射完成注入。
修改后的测试代码:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) @AutoConfigureObservability(tracing = false) class AnnouncementMapperSpec extends Specification { private AService serviceA = Mock() @Autowired AnnouncementMapper mapper @Autowired ForwardMapper forwardMapper def setup() { // 方式1:若ForwardMapper有setter方法 forwardMapper.setServiceA(serviceA) // 方式2:无setter时用反射注入 // def serviceField = ForwardMapper.getDeclaredField("serviceA") // serviceField.setAccessible(true) // serviceField.set(forwardMapper, serviceA) } def "map announcement to schedule"() { given: 1 * serviceA.isEnabled(Value.A) >> true def announcement = new Announcement() // 设置属性... when: Schedule result = mapper.mapForSchedule(announcement) then: // 验证逻辑... } }
内容的提问来源于stack exchange,提问作者Matexon
相关产品推荐
相关产品推荐

