Spring 模拟自连线场in@Import阶级

Spring 模拟自连线场in@Import阶级,spring,unit-testing,Spring,Unit Testing,我正在进行一个单元测试,需要一些由spring注入的对象,因此我使用: @RunWith(SpringRunner.class) @Import({BConfig.class}) public class ATest { private A a; @Autowired private B b; @Before public void init() { a = new A(b); } } 但是,BConfig类有一个我在本测试中

我正在进行一个单元测试,需要一些由spring注入的对象,因此我使用:

@RunWith(SpringRunner.class)
@Import({BConfig.class})
public class ATest {
    private A a;

    @Autowired
    private B b;

    @Before
    public void init() {
        a = new A(b);
    }
}
但是,BConfig类有一个我在本测试中不需要的自动连接字段c:

class BConfig {
    @Autowired
    C c;

    @Bean
    public getB() {
        return new B();
    }

    // other method code will use field c
}
autowired c字段将从@PostConstruct中的redis获取单元测试中不存在的数据。如果我没有忽略这一点,单元测试将报告错误,因为redis数据不存在

我有一个解决方案,使C变成2个子类CProduction和CUnitTest,它们都实现接口C,然后在单元测试中使用CUnitTest。然而,这是一种入侵,因为如果不进行单元测试,C的接口将毫无用处

有更好的方法吗?

考虑使用:

@RunWith(SpringRunner.class)
@ContextConfiguration({BConfig.class})
class ATest {

   @MockBean // will place a mock implementation of C to the context instead of real one that tries to connect with Redis.
   C c;

   ...
}
注意,如果C是一个接口,它将无缝工作。否则,它将尝试通过继承(从C扩展而来的东西)创建代理,如果C在构造函数中有一些与redis相关的代码,它可能仍会尝试调用它

更新1

根据OP的评论,试图澄清答案:

当spring应用程序上下文启动时,它基本上解析所有bean并注入所需内容

首先,它解析bean定义(元数据)——真正的类、范围等。 然后它按照允许注入的顺序创建bean。例如,它的类A有一个类B的字段(都是bean),然后spring必须先创建B,然后创建A并将B注入A

现在,
@MockBean
是一个与测试相关的钩子。它告诉测试中使用的spring应用程序上下文,spring可以从
@Configuration
类中解析出一个常规的bean定义,或者通过放置在其上的
@component
注释找到组件,而不是使用Mock,这是在运行时通过Mockito等框架生成的

此模拟实现稍后可用于指定期望值(请参阅Mockito基础教程,internet上有很多),但更重要的是,它不会连接到redis,因为此模拟实现没有任何与redis相关的代码

更新2

配置应重写如下:

@Configuration
public class BConfig {

    @Bean
    public getB() {
        return new B();
    }
    @Bean
    public C c() {
       return new C();
    }


    // other method code will use field c:
    // if for example D uses C: then you can:

    @Bean
    public D d(C c) {
     return new D(c);
    } 
}
考虑使用:

@RunWith(SpringRunner.class)
@ContextConfiguration({BConfig.class})
class ATest {

   @MockBean // will place a mock implementation of C to the context instead of real one that tries to connect with Redis.
   C c;

   ...
}
注意,如果C是一个接口,它将无缝工作。否则,它将尝试通过继承(从C扩展而来的东西)创建代理,如果C在构造函数中有一些与redis相关的代码,它可能仍会尝试调用它

更新1

根据OP的评论,试图澄清答案:

当spring应用程序上下文启动时,它基本上解析所有bean并注入所需内容

首先,它解析bean定义(元数据)——真正的类、范围等。 然后它按照允许注入的顺序创建bean。例如,它的类A有一个类B的字段(都是bean),然后spring必须先创建B,然后创建A并将B注入A

现在,
@MockBean
是一个与测试相关的钩子。它告诉测试中使用的spring应用程序上下文,spring可以从
@Configuration
类中解析出一个常规的bean定义,或者通过放置在其上的
@component
注释找到组件,而不是使用Mock,这是在运行时通过Mockito等框架生成的

此模拟实现稍后可用于指定期望值(请参阅Mockito基础教程,internet上有很多),但更重要的是,它不会连接到redis,因为此模拟实现没有任何与redis相关的代码

更新2

配置应重写如下:

@Configuration
public class BConfig {

    @Bean
    public getB() {
        return new B();
    }
    @Bean
    public C c() {
       return new C();
    }


    // other method code will use field c:
    // if for example D uses C: then you can:

    @Bean
    public D d(C c) {
     return new D(c);
    } 
}

我不明白。我不会在测试中使用c字段,相反,对象c是我导入的类对象BConfig的字段,它依赖于仅在生产环境中可用的redis数据。我想要mock c,但是,我不能修改BConfig类。请看更新的答案,我希望它澄清了这一点。我得到了这个。然而,还有另一个问题:如何在c中设置数据成员值?该值将在创建上下文期间使用,因此我无法编写类似Mockito.when(c.getNum())的代码。然后在“@Before”方法中返回(1),该方法将在上下文准备之后运行。顺便说一句,C.class应该包含在“@ContextConfiguration”中,否则spring将报告关于上下文配置中的C.class的错误,我认为
BConfig
应该用
@configuration
进行注释,并且您应该在其中的某个地方创建一个C的实例,(使用@Bean注释的方法),那么自动布线就没有意义了……我不明白这一点。我不会在测试中使用c字段,相反,对象c是我导入的类对象BConfig的字段,它依赖于仅在生产环境中可用的redis数据。我想要mock c,但是,我不能修改BConfig类。请看更新的答案,我希望它澄清了这一点。我得到了这个。然而,还有另一个问题:如何在c中设置数据成员值?该值将在创建上下文期间使用,因此我无法编写类似Mockito.when(c.getNum())的代码。然后在“@Before”方法中返回(1),该方法将在上下文准备之后运行。顺便说一句,C.class应该包含在“@ContextConfiguration”中,否则spring将报告关于上下文配置中的C.class的错误,我认为
BConfig
应该用
@configuration
进行注释,并且您应该在其中的某个地方创建一个C的实例,(使用@Bean注释的方法),那么自动布线就没有意义了。。。。