如何为 类 编写单元测试用例,在 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
。如何为此配置创建单元测试用例 类。
- 我们可以为
@Configuration
Class 编写单元测试用例吗?
- 如果可以,我们需要如何发展。
我更喜欢在 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()); }
}
请注意,我们在 B
和 C
的定义中调用 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)
}
}
我有一个 配置 Class,它为 RedissonClient
创建了 bean,还创建了 CacheManager
。如何为此配置创建单元测试用例 类。
- 我们可以为
@Configuration
Class 编写单元测试用例吗? - 如果可以,我们需要如何发展。
我更喜欢在 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()); }
}
请注意,我们在 B
和 C
的定义中调用 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)
}
}