You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在嵌套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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 15:35:08