防止 Application / CommandLineRunner 类在 JUnit 测试期间执行
Posted
技术标签:
【中文标题】防止 Application / CommandLineRunner 类在 JUnit 测试期间执行【英文标题】:Prevent Application / CommandLineRunner classes from executing during JUnit testing 【发布时间】:2015-06-03 08:33:24 【问题描述】:如果在您的 TestCase 类中有这样的注释:
@SpringApplicationConfiguration(classes = Application.class)
这将导致实现CommandLineRunner
接口的Application.class
运行所需的方法
public void run(String... args) throws Exception
我仍然认为这主要是不想要的行为,因为在您的测试环境中,您可能不想启动整个应用程序。
我想到了两个解决这个问题的方法:
-
从我的
Application
类中删除CommandLineRunner
接口
拥有不同的测试环境
这两种解决方案都需要大量编码。 你有更方便的解决方案吗?
【问题讨论】:
你能举一个你想测试的例子吗?如果您想在没有整个应用程序的情况下测试某些内容,则不需要注释。 我想测试整个应用程序(假设使用所有已注册的 bean)。由于CommandLineRunner
接口实现,我不喜欢整个应用程序启动。应该有一种方法可以在不启动run
方法的情况下加载这样的应用程序类。
这正是我的观点,这是一个不需要的副作用,或者至少 SpringRunner 应该让你选择阻止它。并在测试中的所有内容都设置好后启动上下文。
【参考方案1】:
Jan 的解决方案可以更容易实现。
在您的测试类中,激活“测试”配置文件:
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
public class MyFancyTest
在您的 CommandLineRunner 中将配置文件设置为不测试:
@Component
@Profile("!test")
public class JobCommandLineRunner implements CommandLineRunner
那么您不必在应用程序中手动设置配置文件。
【讨论】:
这是否意味着对于每个测试文件我都应该添加一个 @ActiveProfiles("test") 注释? 是的@dnim。对于您要运行的每组测试。 仅仅为了使其可测试而更改生产代码并不是一个好主意。甚至配置文件名称“!test”也是经过验证的生产代码的模仿。 我更喜欢 Jan Petzold 的setAdditionalProfiles()
方式,因为 @ActiveProfiles
不允许您添加其他配置文件。我经常需要在不同的环境中运行测试,即。在开发机器上和 CI 服务器内。【参考方案2】:
如 spring 文档 http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html 中所述,您可以将 @ContextConfiguration 与特殊的初始化程序一起使用:
ConfigFileApplicationContextInitializer 是一个 ApplicationContextInitializer,可应用于您的测试以加载 Spring Boot application.properties 文件。当您不需要 @SpringApplicationConfiguration 提供的全部功能时,可以使用它。
在此示例中,anyComponent 已初始化并注入属性,但不会执行 run(args) 方法。 (Application.class 是我的主要 Spring 入口点)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class,
initializers = ConfigFileApplicationContextInitializer.class)
public class ExtractorTest
@Autowired
AnyComponent anyComponent;
@Test
public void testAnyComponent()
anyComponent.anyMethod(anyArgument);
【讨论】:
CommandLineRunner 没问题,但 ApplicationRunner 呢?我无法让 SpringBootApplication(实现 ApplicationRunner)只运行一次:第一个自动不带参数,第二个手动使用我需要的参数。【参考方案3】:您可以在与您的应用程序相同的包中定义一个测试配置,它看起来完全相同,只是它不包括实现 CommandLineRunner 的 bean。这里的关键是@ComponentScan.excludeFilters:
@Configuration
@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = CommandLineRunner.class))
@EnableAutoConfiguration
public class TestApplicationConfiguration
然后,只需替换测试中的配置:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = TestApplicationConfiguration.class)
public class SomeApplicationTest
...
现在不会执行 CommandLineRunner,因为它们不是配置的一部分。
【讨论】:
这个答案非常聪明,只是漏掉了一点:您还必须排除excludeFilters
属性中的Application.class
,否则它会扫描您尝试排除的bean。
SpringApplicationConfiguration
@deprecated as of 1.4 in support SpringBootTest
.可以替换为@SprinBootTest(classes = TestApplicationConfiguration.class)
排除 Application.class 也将排除您可能/可能不需要的所有 bean。
请举一个排除Application.class的例子?
我想我们可以省略注解@RunWith(SpringJUnit4ClassRunner.class)【参考方案4】:
我参加聚会有点晚了,但一个合理的方法是用@ConditionalOnProperty
标记bean,例如
@ConditionalOnProperty(prefix = "job.autorun", name = "enabled", havingValue = "true", matchIfMissing = true)
public CommandLineRunner myRunner() ...
以下注释将在测试中禁用它:
@SpringBootTest(properties = "job.autorun.enabled=false")
【讨论】:
当您仍然需要在测试前使用 Main 类上的注解来配置应用程序但需要避免特定 bean 运行时,这是最好的解决方案。非常适合具有“启动”所有内容的 bean 的命令行应用程序。谢谢。【参考方案5】:如果您安装了模拟框架(例如 MockMVC),您可以创建 CommandLineRunner 实现的模拟实例,或多或少地禁用它:
@MockBean 私有 TextProcessor myProcessor;
【讨论】:
这个答案也适合@EventListener(ApplicationReadyEvent.class)
的方法【参考方案6】:
以前的答案对我不起作用。我最终使用了不同的配置文件 - Spring Boot 中的 init 方法示例:
SpringApplication app = new SpringApplication(AppConfig.class);
app.setAdditionalProfiles("production");
app.run(args);
这不会在测试期间执行,所以我们在这里很安全。
所有测试都有自己的配置文件“test”(这在许多其他方面也很有用):
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("test")
public class MyFancyTest
命令行运行器使用“生产”配置文件进行注释,因此测试会忽略它:
@Component
@Profile("production")
public class JobCommandLineRunner implements CommandLineRunner
【讨论】:
【参考方案7】:我通过不实现 CommandLineRunner 来解决这个问题。只需从上下文中获取一个 bean,并在其上调用一个方法,传递 argv。这样您将获得相同的结果,并且在运行测试时应用程序不会自动启动。
【讨论】:
以上是关于防止 Application / CommandLineRunner 类在 JUnit 测试期间执行的主要内容,如果未能解决你的问题,请参考以下文章
为啥我必须在 AWS Application Load Balancer 中禁用 HTTP/2 以防止出现 ERR_SPDY_PROTOCOL_ERROR?