如何为 类 编写单元测试用例,在 Spring Boot 应用程序中用 @Configuration 注释

How to write the unit test case for the Classes which are annotated with @Configuration in Spring Boot application

我有一个 配置 Class,它为 RedissonClient 创建了 bean,还创建了 CacheManager。如何为此配置创建单元测试用例 类。

  1. 我们可以为 @Configuration Class 编写单元测试用例吗?
  2. 如果可以,我们需要如何发展。

我更喜欢在 Spock Framework 中编写测试用例,Groovy。如果没有,请使用 Junit 或 Mockito 框架。如何为 Spring Boot application

中用 @Configuration 注释的 Classes 编写单元测试用例
@Configuration
public class CacheConfiguration {
    private static final String CONFIG= "Configuration";

    @Value("${redis.server.url}")
    private String redisUrl;

    @Value("${redis.server.password}")
    private String password;

    @Bean 
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress(redisUrl).setPassword(password);
        RedissonClient client = Redisson.create(config);
        return client;
    }
    @Bean
    public CacheManager redissonCacheManager(RedissonClient redissonClient) {
        Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
        config.put(CONFIG, new CacheConfig(24*60*1000, 12*60*1000));
        return new RedissonSpringCacheManager(redissonClient, config);
    }

}

我想你应该意识到用 @Configuration 注释的 class 并不是真正的 java class,或者至少你不应该这样对待它们。我知道这听起来有争议,我会解释...

所以历史上 spring 使用 XML 配置来声明 bean。

在 spring 2.5 中,我猜,他们引入了一种基于注释的方法,您可以在 classes 上放置注释 @Component/@Service,然后将 @Autowired 在任何你想要 spring 注入依赖项的地方,然后 spring 开始,扫描 class 路径,检测 bean 并使用这些 bean 启动应用程序上下文。

然后Spring3.0引入了Java配置方式做spring相关配置:@Configuration / @Bean在一个特殊的class.

所以您应该将这些配置 classes 视为 "substitution" 我之前描述的方法

现在让我问一下,您认为您应该自己测试 XML bean 配置吗?可能不会... 你认为你应该测试 class 上面有注释 @Component 并且所有必要的依赖项都是自动装配的(通过反射或其他)?可能不是。

那你为什么要测试 Java 配置 classes?

这是您可能会感兴趣的另一个论点

我已经说过这些 classes 仅用于 spring 解析 bean 并且它在内部 运行s 它们。但是 spring 不只是 "run" 它们 - 它为它们创建了 运行-time wrapper 来克服一些技术问题。这是一个这样的例子:

假设我们有三个 bean:A、B、C,例如 B 和 C 依赖于 A。


class A {}

class B {
   private A a;
   public B(A a) {this.a = a;}
}

class C {
   private A a;
   public C(A a) {this.a = a;}
}

所有 bean 都应该是单例的,所以我们这样定义配置:

@Configuration
public class MyConfig { 
   @Bean
   public A a() { return new A(); }

   @Bean
   public B b() { return new B(a()); }

   public C c() {return new C(a()); }

} 

请注意,我们在 BC 的定义中调用 a() 现在,让我问你一个问题:如果它是一个 "regular" java 代码,方法 a() 的两个不同调用(在 B 和 C 的构造函数中)分别应该如何 return A 的同一个实例

所以 spring 确实使用了很多复杂的代码,这实际上是 运行s 运行time,而不是你的 class 原样,但是它的转换版本,你永远不知道这些转换到底是什么,因为它和内部 spring 东西。但是如此 按原样测试它有什么意义?

我相信还有更多这样的争论,但要点很明确 - 不要单独测试配置 classes。

相反,您可以使用集成测试 运行 spring 容器并加载配置中所需的所有 classes。但是,在这种情况下,您可能想要模拟一些 classes(例如使用 @MockBean),因此它不会是 100% 准确的测试。

就代码覆盖率而言 - 在我看来,您应该将这些 class 完全排除在覆盖范围之外

经过一些研究发现,我们可以运行嵌入redis服务器,我们可以通过启动应用程序来检查我们是否能够连接到redis服务器。我不知道这是否正确。但是这样做的话,确实需要时间才能完成,大约需要20秒左右。使用以下依赖项来测试这个 // https://mvnrepository.com/artifact/it.ozimov/embedded-redis testCompile 组:'it.ozimov',名称:'embedded-redis',版本:'0.7.2'

@SpringBootTest(classes = [TestApp])
class CacheConfigurationSpec extends Specification {

@Shared
RedisServer redisServer;

def setupSpec() {
    redisServer = RedisServer.builder()
            .port(6379)
            .setting("bind 127.0.0.1")
            .setting("maxmemory 128M")
            .build()
    redisServer.start()
}

def cleanupSpec() {
    if (redisServer != null) {
        redisServer.stop()
    }
}

@Autowired
private RedissonClient redissonClient;

def "load all contexts"() {

}
}
@SpringBootApplication
class TestApp {
 static void main(String[] args){
    SpringApplication.run(TestApp.class, args)
 }
}