防止在Junit测试期间执行ApplicationRunner或CommandLineRunner bean
最后修改:2021年1月16日
1.概述
在本教程中,我们将展示如何防止类型为ApplicationRunner或CommandLineRunner在Spring Boot集成测试期间运行。
2.示例应用程序
我们的示例应用程序由命令行运行程序、应用程序运行程序和任务服务bean组成。
命令行运行程序调用任务服务的执行方法,以便在应用程序启动时执行任务:
@Component public class CommandLineTaskExecutor implements CommandLineRunner {private TaskService TaskService;public CommandLineTaskExecutor(TaskService TaskService) {this。taskService = taskService;} @Override public void run(String…taskService. args)抛出异常。执行(“命令行运行器任务”);}}
以同样的方式,应用程序运行程序与任务服务交互以执行另一个任务:
@Component public class ApplicationRunnerTaskExecutor implements ApplicationRunner {private TaskService TaskService;public ApplicationRunnerTaskExecutor(TaskService TaskService) {this。taskService = taskService;} @Override public void run(ApplicationArguments args) throws Exception {taskService。执行(“应用程序跑任务”);}}
最后,任务服务负责执行客户端的任务:
@Service public class TaskService {private static Logger Logger = LoggerFactory.getLogger(TaskService.class);public void execute(String task) {log .info("do " + task);}}
而且,我们也有一个Spring Boot应用程序类,使它所有工作:
@SpringBootApplication public class ApplicationCommandLineRunnerApp {public static void main(String[] args) {SpringApplication.run(ApplicationCommandLineRunnerApp.class, args);}}
3.测试预期行为
的ApplicationRunnerTaskExecutor和CommandLineTaskExecutor在Spring Boot加载应用程序上下文后运行。
我们可以用一个简单的测试来验证这一点:
@SpringBootTest class RunApplicationIntegrationTest {@SpyBean ApplicationRunnerTaskExecutor ApplicationRunnerTaskExecutor;@SpyBean CommandLineTaskExecutor CommandLineTaskExecutor;@Test void whenContextLoads_thenRunnersRun() throws Exception {verify(applicationRunnerTaskExecutor, times(1)).run(any());验证(commandLineTaskExecutor,乘以(1).run (());}}
如我们所见,我们使用SpyBean注释应用5间谍到ApplicationRunnerTaskExecutor和CommandLineTaskExecutorbean。通过这样做,我们可以验证运行每个豆子的方法被调用一次。
在下一节中,我们将看到在Spring Boot集成测试期间防止这种默认行为的各种方法和技术。
4.通过Spring配置文件的预防
阻止这两个程序运行的一种方法是用@Profile:
@Profile("!test") @Component public class CommandLineTaskExecutor implements CommandLineRunner{//和前面一样}
@Profile("!test") @Component public class ApplicationRunnerTaskExecutor implements ApplicationRunner{//和前面一样}
在进行了上述修改之后,我们继续进行集成测试:
@ActiveProfiles("test") @SpringBootTest class RunApplicationWithTestProfileIntegrationTest {@Autowired private ApplicationContext context;@Test void whenContextLoads_thenRunnersAreNotLoaded() {assertnotull (context.getBean(TaskService.class));assertThrows(NoSuchBeanDefinitionException.class, () -> context.getBean(CommandLineTaskExecutor.class), "CommandLineRunner不应该被加载在这个集成测试");assertThrows(NoSuchBeanDefinitionException.class, () -> context.getBean(ApplicationRunnerTaskExecutor.class), "ApplicationRunner不应该在这个集成测试期间被加载");}}
正如我们看到的,我们将上面的测试类注释为@ActiveProfiles(“测试”)注释,这意味着它不会连接那些被注释的对象@Profile(“!测试”)。结果,两者都没有CommandLineTaskExecutor豆也ApplicationRunnerTaskExecutor豆子是装载的。
5.预防通过ConditionalOnProperty注释
或者,我们可以通过属性配置它们的连接,然后使用ConditionalOnProperty注释:
@ConditionalOnProperty(prefix = "application.runner", value = "enabled", havingValue = "true", matchIfMissing = true)
@ConditionalOnProperty(prefix = "command.line.runner", value = "enabled", havingValue = "true", matchIfMissing = true) @Component public class CommandLineTaskExecutor implements CommandLineRunner{//和前面一样}
正如我们看到的,的ApplicationRunnerTaskExecutor和CommandLineTaskExecutor默认启用,我们可以禁用它们,如果我们设置以下属性假:
- command.line.runner.enabled
- application.runner.enabled
在我们的测试中,我们将这些属性设置为假,既不ApplicationRunnerTaskExecutor也没有CommandLineTaskExecutorbean被加载到应用程序上下文中:
@SpringBootTest(properties = {"command.line.runner.enabled=false", "application.runner.enabled=false"}) class RunApplicationWithTestPropertiesIntegrationTest{//与之前相同}
现在,尽管上述技术帮助我们实现了我们的目标,但在某些情况下,我们希望测试所有Spring bean都已正确加载和连接。
例如,我们可能想要测试TaskService正确地将Bean注入CommandLineTaskExecutor,但我们仍然不想要它运行方法在我们的测试期间执行。所以,让我们看看最后一节,它解释了我们如何实现这一点。
6.通过不引导整个容器来预防
在这里,我们将描述如何预防CommandLineTaskExecutor和ApplicationRunnerTaskExecutor通过不引导整个应用程序容器来执行bean。
在前几节中,我们使用@SpringBootTest注释,这导致在集成测试期间启动整个容器。@SpringBootTest包括两个元注释与最后一个解决方案相关的:
@BootstrapWith (SpringBootTestContextBootstrapper.class) @ExtendWith (SpringExtension.class)
好吧,如果在我们的测试期间不需要引导整个容器,那么就不希望使用@BootstrapWith。
相反,我们可以用@ContextConfiguration:
@ContextConfiguration(classes = {ApplicationCommandLineRunnerApp.class}, initializer = ConfigDataApplicationContextInitializer.class)
与@ContextConfiguration,我们决定如何为集成测试加载和配置应用程序上下文。通过设置ContextConfiguration类属性时,我们声明Spring Boot应该使用ApplicationCommandLineRunnerApp类装入应用程序上下文。通过将初始化式定义为ConfigDataApplicationContextInitializer,应用程序加载其属性。
我们仍然需要@ExtendWith (SpringExtension.class)因为它将Spring TestContext框架集成到JUnit 5的Jupiter编程模型中。
由于上述原因,Spring Boot应用程序上下文加载应用程序的组件和属性,而不执行CommandLineTaskExecutor或者是ApplicationRunnerTaskExecutor豆:
@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {ApplicationCommandLineRunnerApp.class}, initializers = ConfigDataApplicationContextInitializer.class)公共类LoadSpringContextIntegrationTest {@SpyBean TaskService TaskService;@SpyBean CommandLineRunner CommandLineRunner;@SpyBean ApplicationRunner ApplicationRunner;@Test void whenContextLoads_thenRunnersDoNotRun() throws Exception {assertNotNull(taskService);assertNotNull (commandLineRunner);assertNotNull (applicationRunner);验证(taskService,乘以(0)). execute (());验证(commandLineRunner,乘以(0)).run (());验证(applicationRunner,乘以(0)).run (());}}
同时,我们必须记住这一点的ConfigDataApplicationContextInitializer,当单独使用时,不提供支持@ value (“${...}”)注入。如果我们想要支持它,我们必须配置一个PropertySourcesPlaceholderConfigurer。
7.结论
在本文中,我们展示了防止执行ApplicationRunner和CommandLineRunnerSpring Boot集成测试期间的bean。
与往常一样,代码是可用的在GitHub。