在 Spring 测试中禁用 @EnableScheduling

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29014496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 00:25:38  来源:igfitidea点击:

Disable @EnableScheduling on Spring Tests

springunit-testingjunitjunit4springjunit4classrunner

提问by Robbo_UK

When I run my unit tests, it invokes my scheduled tasks. I want to prevent this behaviour, which is caused by the fact that I have @EnableSchedulingon my main app configuration.

当我运行单元测试时,它会调用我的计划任务。我想防止这种行为,这是由于我@EnableScheduling在主应用程序配置上的事实造成的。

How can I disable this on my unit tests?

如何在我的单元测试中禁用它?

I have come across this question/answerwhich suggests setting up profiles?

我遇到过这个问题/答案,它建议设置配置文件?

Not sure how I would go about that? or if its an overkill? I was thinking of having a separate AppConfiguration for my unit tests but it feels like im repeating code twice when I do that?

不知道我会怎么做?或者如果它是矫枉过正?我正在考虑为我的单元测试使用单独的 AppConfiguration,但是当我这样做时感觉就像我重复了两次代码?

@Configuration
@EnableJpaRepositories(AppConfiguration.DAO_PACKAGE)
@EnableTransactionManagement
@EnableScheduling
@ComponentScan({AppConfiguration.SERVICE_PACKAGE,
                AppConfiguration.DAO_PACKAGE,
                AppConfiguration.CLIENT_PACKAGE,
                AppConfiguration.SCHEDULE_PACKAGE})
public class AppConfiguration {

    static final    String MAIN_PACKAGE             = "com.etc.app-name";
    static final    String DAO_PACKAGE              = "com.etc.app-name.dao";
    private static  final  String ENTITIES_PACKAGE  = "com.etc.app-name.entity";
    static final    String SERVICE_PACKAGE          = "com.etc.app-name.service";
    static final    String CLIENT_PACKAGE           = "com.etc.app-name.client";
    static final    String SCHEDULE_PACKAGE         = "com.etc.app-name.scheduling";


    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
       // stripped code for question readability
    }

    // more app config code below etc

}

Unit test example.

单元测试示例。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={AppConfiguration.class})
@Transactional
@TransactionConfiguration(defaultRollback = true)
@WebAppConfiguration
public class ExampleDaoTest {

    @Autowired
    ExampleDao exampleDao;

    @Test
    public void testExampleDao() {
        List<Example> items = exampleDao.findAll();
        Assert.assertTrue(items.size()>0);
    }
}

回答by Marko Vranjkovic

If you don't want to use profiles, you can add flag that will enable/disable scheduling for the application

如果您不想使用配置文件,您可以添加标志来启用/禁用应用程序的调度

In your AppConfigurationadd this

在你AppConfiguration添加这个

  @ConditionalOnProperty(
     value = "app.scheduling.enable", havingValue = "true", matchIfMissing = true
  )
  @Configuration
  @EnableScheduling
  public static class SchedulingConfiguration {
  }

and in your test just add this annotation to disable scheduling

并在您的测试中添加此注释以禁用调度

@TestPropertySource(properties = "app.scheduling.enable=false")

回答by yankee

An alternative would be to unregister the bean post processor that schedules the events. This can be done by simply putting the following class on the classpath of your tests:

另一种方法是取消注册调度事件的 bean 后处理器。这可以通过简单地将以下类放在测试的类路径中来完成:

public class UnregisterScheduledProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
        for (String beanName : beanFactory.getBeanNamesForType(ScheduledAnnotationBeanPostProcessor.class)) {
            ((DefaultListableBeanFactory)beanFactory).removeBeanDefinition(beanName);
        }
    }
}

While this is quite simple and seems to do the job, beware that I did not test this very much or check for possible implications of removing a defined bean from the registry or making sure that ordering of PostProcessors won't be an issue...

虽然这很简单并且似乎可以完成这项工作,但请注意我没有对此进行过多测试或检查从注册表中删除定义的 bean 或确保后处理器的排序不会成为问题的可能影响......

回答by Laila Sharshar

I just parameterized my @Scheduled annotation with configurable delay times:

我只是用可配置的延迟时间参数化了我的 @Scheduled 注释:

@Scheduled(fixedRateString = "${timing.updateData}", initialDelayString = "${timing.initialDelay}")

In my test application.yaml:

在我的测试 application.yaml 中:

timing:
    updateData: 60000
    initialDelay: 10000000000

And main application.yaml:

和主要的application.yaml:

timing:
    updateData: 60000
    initialDelay: 1

It's not turning it off but creating such a long delay, the tests will be long over before it runs. Not the most elegant solution but definitely one of the easiest I've found.

它不是关闭它,而是造成了如此长的延迟,测试将在它运行之前很久就结束了。不是最优雅的解决方案,但绝对是我发现的最简单的解决方案之一。

回答by Vojtech Ruzicka

In each Test you define which spring configuration should be used, currently you have:

在每个测试中,您定义应使用哪种弹簧配置,目前您有:

@ContextConfiguration(classes={AppConfiguration.class})

Common practice is to define separate spring configuration for your normal application and for your tests.

通常的做法是为您的正常应用程序和测试定义单独的弹簧配置。

AppConfiguration.java 
TestConfiguration.java

Then in your test you simply refference TestConfigurationinstead of your current AppConfigurationusing @ContextConfiguration(classes={TestConfiguration.class})

然后在您的测试中,您只需参考TestConfiguration而不是当前AppConfiguration使用@ContextConfiguration(classes={TestConfiguration.class})

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={TestConfiguration.class})
@Transactional
@TransactionConfiguration(defaultRollback = true)
@WebAppConfiguration
public class ExampleDaoTest

This way you can configure any setting for your tests differently than in production code. You can for example use in-memory database for your tests instead of regular one and much more.

通过这种方式,您可以为测试配置与生产代码不同的任何设置。例如,您可以使用内存数据库来进行测试,而不是常规数据库等等。

回答by Gladson Bruno

I was able to solve this problem by creating a method that removes the scheduled tasks during unit tests. Here is an example:

我能够通过创建一种在单元测试期间删除计划任务的方法来解决这个问题。下面是一个例子:

    import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
    import org.springframework.context.ApplicationContext;

    public static void removeSchedulledTasks(ScheduledAnnotationBeanPostProcessor postProcessor, ApplicationContext appContext) {

    postProcessor.setApplicationContext(appContext);

    Iterator<ScheduledTask> iterator = postProcessor.getScheduledTasks().iterator();

    while(iterator.hasNext()) {

        ScheduledTask taskAtual = iterator.next();
        taskAtual.cancel();

    }

}

Use example:

使用示例:

import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.Utils;


@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRemoveScheduller {


    @Autowired
    private ScheduledAnnotationBeanPostProcessor postProcessor;

    @Autowired
    private ApplicationContext appContext;


    @Before
    public void init(){

        //Some init variables

        //Remove schedulled tasks method
        Utils.removeSchedulledTasks(postProcessor, appContext);

    }

    //Some test methods

}

Hope this helps.

希望这可以帮助。

回答by Shafiul

One more solution without any change in production code, using the @MockBean.

使用@MockBean.

@RunWith(SpringRunner.class)
@SpringBootTest
@MockBean(MyScheduledClass.class)
public class MyTest {

Which will eventually replace active scheduled job or create a mocked one.

这最终将取代活动的预定作业或创建一个模拟作业。

From the documentation

从文档

Mocks can be registered by type or by {@link #name() bean name}. Any existing single bean of the same type defined in the context will be replaced by the mock, if no existing bean is defined a new one will be added.

模拟可以通过类型或通过 {@link #name() bean name} 注册。任何在上下文中定义的相同类型的现有单个 bean 都将被模拟替换,如果没有定义现有 bean,将添加一个新 bean。