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

如何为带存储数据的类配置Spring依赖注入并支持测试Mock?

官方解决方案推荐

第一步:优化Adapter的依赖注入方式(官方最佳实践)

先把Adapter改成全构造函数注入,这是Spring 4.3+官方推荐的方式,既避免字段注入的弊端,又能让依赖关系完全透明,同时兼顾Spring上下文注入和测试灵活性:

@Service
@Scope("prototype")
final class Adapter {
    private final AdapterService adapterService;
    private final Options options;

    // Spring 4.3+ 无需@Autowired,会自动解析构造函数参数
    Adapter(@NotNull AdapterService adapterService, @NotNull Options options) {
        this.adapterService = adapterService;
        this.options = options;
    }
}

这样做的好处:

  • 单元测试时可以直接手动实例化,无需依赖Spring上下文:
    @Test
    void unitTestAdapter() {
        AdapterService mockService = mock(AdapterService.class);
        Options options = new Options();
        Adapter adapter = new Adapter(mockService, options);
        
        // 直接测试adapter逻辑,验证mock交互
    }
    
  • Spring上下文依然可以正常创建原型Bean,调用getBean(Adapter.class, options)时,Spring会自动注入容器中的AdapterService实例,同时使用你传入的Options。

第二步:集成测试中注入Mock服务(Spring官方支持)

如果必须依赖Spring上下文进行集成测试,有两种官方方案:

方案1:Spring Boot测试用@MockBean

在测试类上用@MockBean注解替换容器中的AdapterService为Mock实例,Spring会自动将这个Mock注入到新创建的Adapter原型Bean中:

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.verify;

@SpringBootTest
class AdapterIntegrationTest {

    @MockBean
    private AdapterService mockAdapterService;

    @Test
    void testAdapterWithMockService() {
        Options options = new Options(); // 构造你的业务参数
        Adapter adapter = applicationContext.getBean(Adapter.class, options);
        
        // 调用adapter的业务方法
        adapter.doSomething();
        
        // 验证Mock服务的交互是否符合预期
        verify(mockAdapterService).expectedMethodCall();
    }
}
方案2:纯Spring测试用测试专用配置类

如果不用Spring Boot,可通过@Configuration类在测试上下文里注册Mock服务:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import static org.mockito.Mockito.mock;

@SpringJUnitConfig
@Import(AdapterIntegrationTest.TestConfig.class)
class AdapterIntegrationTest {

    @Configuration
    static class TestConfig {
        // 注册Mock的AdapterService到测试上下文
        @Bean
        AdapterService adapterService() {
            return mock(AdapterService.class);
        }
    }

    @Test
    void testAdapterWithMockService() {
        Options options = new Options();
        Adapter adapter = applicationContext.getBean(Adapter.class, options);
        
        // 执行测试逻辑并验证
    }
}

为什么不推荐临时方案?

你提到的临时方案(测试专用构造函数、Setter、反射)都存在弊端:

  • 测试专用构造函数/Setter会污染生产代码,增加维护成本
  • 反射破坏封装性,代码脆弱且可读性差

而上面的官方方案完全遵循Spring的设计原则,既不修改生产代码的语义,又能优雅实现测试需求。

内容的提问来源于stack exchange,提问作者Christian Bueche

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 12:37:32