防止 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?

springboottest如何防止运行应用程序

如何在 iOS 上使用 swift 防止我的​​应用程序屏幕锁定

WPF防止重复运行实例

WPF防止重复运行实例

RAISE_APPLICATION_ERROR 问题