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

Posted

技术标签:

【中文标题】如何在 Spring Boot 应用程序中为带有 @Configuration 注释的类编写单元测试用例【英文标题】:How to write the unit test case for the Classes which are annotated with @Configuration in Spring Boot application 【发布时间】:2020-05-26 19:08:47 【问题描述】:

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

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

我更喜欢用 Groovy 在 Spock Framework 中编写测试用例。如果没有,请使用 Junit 或 Mockito 框架。如何为Spring Boot应用中@Configuration注解的类编写单元测试用例

@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);
    


【问题讨论】:

单独测试它们没有真正意义,因为它们只在 Spring 应用程序中有意义。 是的。我同意,假设我们使用其他逻辑构建应用程序,那么我们需要如何确保覆盖使用 @Configuration 注释的类。 您可以像任何其他类一样对它进行单元测试,即通过调用其公共方法并验证结果,例如redissonClient() 实际上返回具有预期配置的 RedissonClient 实例。如果你想测试例如私有成员的值,这将是一个您也可以轻松编写的集成测试。但是问问自己,你是否真的想测试你的应用程序或者 Spring 容器是否有效。 @kriegaex 你能检查我的答案,并建议我的答案 @m-deinum :你能检查我的答案,并建议我的答案 - 【参考方案1】:

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

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

我猜在 spring 2.5 中,他们引入了一种基于注解的方法,您将注解 @Component/@Service 放在类上,将 @Autowired 放在您希望 spring 注入依赖项的任何位置,然后 spring 启动,扫描类路径,检测 bean 并使用这些 bean 启动应用程序上下文。

然后 Spring 3.0 引入了一种 Java Configuration 方式来做 spring 相关的配置:@Configuration / @Bean 在一个特殊的类中。

因此,您应该将这些配置类视为对我之前描述的方法的“替代”

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

那么为什么要测试 Java Config 类呢?

这是另一个你可能会觉得有趣的论点

我说过这些类仅用于 spring 来解析 bean 并在内部运行它们。但是 spring 不只是“运行”它们——它为它们创建了运行时包装器来克服一些技术问题。这是一个这样的例子:

以免假设我们有三个 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() 现在,让我问你一个问题:如果它是一个“常规”Java 代码,那么两个不同的方法 a()(在 B 和 C 的构造函数中)调用应该如何分别返回 @ 的 相同实例 987654334@?

所以 spring 确实使用了很多复杂的代码,这是在运行时实际运行的代码,不是你的类,而是它的转换版本,你永远不知道这些转换到底是什么它和内部弹簧的东西。但是,测试它的意义何在?

我相信还有更多这样的论点,但重点很清楚——不要自己测试配置类。

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

就代码覆盖率而言 - IMO 您应该将这些类完全排除在覆盖范围之外

【讨论】:

:感谢您提供的信息,这些信息非常有用。但是我确实看到有些人开发了 Groovy 测试用例,他们在其中启动了 spring 应用程序。就像你说的将运行弹簧容器的集成测试,如果我必须为上述配置做他们我必须做的事情。我对此完全陌生。 Spock 是一个基于 junit4 的 junit。因此,您可以将其与 Spring Boot 测试集成,如下所示:baeldung.com/spring-spock-testing 你能检查我的答案,并建议我的答案。【参考方案2】:

经过一番研究发现,我们可以运行嵌入式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)
 

【讨论】:

如果你这样做,你实际上不需要在@SpringBootTest 中指定classes=[TestApp] - 指定一个带有注册bean 的配置,它只会加载那个配置,并且测试会运行得更快。否则,它会尝试模仿整个应用程序的启动。总而言之,这是一个非常复杂的主题,有很多设施。如果你想真正了解它是如何工作的,我可以推荐这个会话:youtube.com/watch?v=Wpz6b8ZEgcU 另外请注意,该配置可以是可缓存的,因此如果你有很多测试类,spring 可以缓存应用程序。上下文并大大减少时间

以上是关于如何在 Spring Boot 应用程序中为带有 @Configuration 注释的类编写单元测试用例的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Boot / Spring Data 中为 Amazon RDS Mysql 启用 SSL?

我应该如何在 Gradle 中为 Spring Boot 应用程序设置主类?

如何在 Spring Boot 中为 Spring LDAP 身份验证设置覆盖 BindAuthenticator handleBindException

如何从 facebook/google/... oauth2 身份验证在 Spring (Boot) 中为单页应用程序派生自定义登录令牌?

如何在 Spring Boot 中为 Camel 配置 Jackson ObjectMapper

带有查询 Spring-Boot jpa 1.5 的可选参数