sourcetip

스프링 테스트에서 @EnableScheduling 비활성화

fileupload 2023. 3. 29. 21:47
반응형

스프링 테스트에서 @EnableScheduling 비활성화

장치 테스트를 실행하면 예약된 작업이 호출됩니다.는 이런 . 가 이 행동을 가지고 있기 이다.@EnableScheduling내 메인 앱 구성에 있습니다.

유닛 테스트에서 이 기능을 비활성화하려면 어떻게 해야 합니까?

프로파일 설정을 제안하는 질문/답변입니다.

내가 그걸 어떻게 해야할지 모르겠어?아니면 과잉 살상인가요?유닛 테스트를 위해 별도의 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

}

유닛 테스트 예시

@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);
    }
}

프로파일을 사용하지 않을 경우 응용 프로그램의 예약을 활성화/비활성화하는 플래그를 추가할 수 있습니다.

고객님의 고객명AppConfiguration을 덧붙이다

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

테스트에서 이 주석을 추가하여 일정을 비활성화합니다.

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

방금 @Scheduled 주석을 설정 가능한 지연 시간으로 파라미터화했습니다.

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

테스트 어플리케이션.yaml:

timing:
    updateData: 60000
    initialDelay: 10000000000

메인 어플리케이션.yaml:

timing:
    updateData: 60000
    initialDelay: 1

꺼지는 게 아니라 너무 오래 지연돼서 테스트가 끝나기 전에 오래 걸릴 거예요.가장 우아한 솔루션은 아니지만 내가 찾은 가장 쉬운 솔루션 중 하나입니다.

하지 않고 하나의 을 더 수 .@MockBean.

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

이는 결국 활성 예약된 작업을 대체하거나 조롱된 작업을 생성합니다.

매뉴얼에서

Mock은 유형별 또는 {@link #name() bean name}별로 등록할 수 있습니다.컨텍스트에서 정의된 동일한 유형의 기존 단일 빈은 모두 모의로 대체됩니다. 기존 빈이 정의되지 않은 경우 새 빈이 추가됩니다.

다른 방법으로는 이벤트를 스케줄 하는 bean post processor의 등록을 해제하는 방법이 있습니다.이것은, 다음의 클래스를 테스트의 클래스 패스에 배치하는 것만으로 실행할 수 있습니다.

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);
        }
    }
}

이 방법은 매우 간단하고 효과가 있는 것 같습니다만, 저는 이것을 그다지 테스트하지 않았거나, 레지스트리에서 정의된 빈을 삭제하거나, Post Processor를 주문하는 것이 문제가 되지 않는지 확인하려고 합니다.

Spring Boot 식 및 cron 식에서는 스케줄링을 이노블 또는 디세블로 할 수 있습니다.예를 들어 test application.yml을 정의하여

scheduler:
  cron-expr: '-'

'-'를 사용하여 예약을 사용하지 않도록 설정하는 것도 참조하십시오.스케줄러 클래스에서는 식을 전달할 수 있습니다.

@Scheduled(cron = "${scheduler.cron-expr}")

추가가

app.sysloging.enable=false

test application.properties와 함께

@ConditionalOnProperty(value = "app.scheduling.enable", havingValue = "true", matchIfMissing = true)
@EnableScheduling

Marko Vranjkovic의 답변에서와 같이 구성 클래스 주석을 스케줄링하는 것은 각 테스트에 주석을 달 필요 없이 모든 테스트에서 작동합니다.

각 테스트에서 사용해야 하는 스프링 구성을 정의합니다. 현재 다음과 같은 기능이 있습니다.

@ContextConfiguration(classes={AppConfiguration.class})

통상은, 통상의 애플리케이션 및 테스트용의 스프링 구성을 개별적으로 정의하는 것입니다.

AppConfiguration.java 
TestConfiguration.java

그리고 당신의 테스트에서 당신은 단순하게 재차이입니다.TestConfiguration현재가 아닌AppConfiguration사용.@ContextConfiguration(classes={TestConfiguration.class})

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

이렇게 하면 프로덕션 코드와 다르게 테스트 설정을 구성할 수 있습니다.예를 들어 테스트에는 일반 데이터베이스 및 기타 데이터베이스 대신 인메모리 데이터베이스를 사용할 수 있습니다.

유닛 테스트 중에 스케줄된 작업을 삭제하는 방법을 만들어 이 문제를 해결할 수 있었습니다.다음은 예를 제시하겠습니다.

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

    public static void removeScheduledTasks(ScheduledAnnotationBeanPostProcessor postProcessor, ApplicationContext appContext) {
        postProcessor.setApplicationContext(appContext);
        postProcessor.getScheduledTasks().forEach(ScheduledTask::cancel);   
    }
}

사용 예:

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 scheduled tasks method
        Utils.removeScheduledTasks(postProcessor, appContext);
        
    }

    //Some test methods

}

이게 도움이 됐으면 좋겠다.

저는 이것을 (단위 테스트가 아닌) 일반 수업에서 하려고 했습니다.메인 Spring Boot 어플리케이션이 있는데 벌크 데이터 정리를 위해 작은 유틸리티 클래스가 필요했습니다.메인 앱의 전체 애플리케이션 콘텍스트를 사용하고 싶었지만 예약된 작업은 모두 해제하고 싶었습니다.저에게 가장 좋은 솔루션은 Gladson Bruno와 비슷했습니다.

scheduledAnnotationBeanPostProcessor.getScheduledTasks().forEach(ScheduledTask::cancel);

이 접근법의 또 다른 장점은 스케줄링된 모든 작업 목록을 가져올 수 있으며 로직을 추가하여 일부 작업을 취소하고 다른 작업을 취소할 수 없다는 것입니다.

만들다TestTaskScheduler시험 수업의 콩

public class TestTaskScheduler implements TaskScheduler {
    
    private static final NullScheduledFuture NULL_SCHEDULED_FUTURE = new NullScheduledFuture();
    
    @Override
    public ScheduledFuture<?> schedule(Runnable task, Trigger trigger) {
        return NULL_SCHEDULED_FUTURE;
    }

    @Override
    public ScheduledFuture<?> schedule(Runnable task, Date startTime) {
        return NULL_SCHEDULED_FUTURE;
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
        return NULL_SCHEDULED_FUTURE;
    }

    @Override
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
        return NULL_SCHEDULED_FUTURE;
    }

    @Override
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay) {
        return NULL_SCHEDULED_FUTURE;
    }

    @Override
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay) {
        return NULL_SCHEDULED_FUTURE;
    }
    
    private static class NullScheduledFuture implements ScheduledFuture {
        
        @Override
        public long getDelay(TimeUnit unit) {
            return 0;
        }

        @Override
        public int compareTo(Delayed o) {
            return 0;
        }

        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            return false;
        }

        @Override
        public boolean isCancelled() {
            return false;
        }

        @Override
        public boolean isDone() {
            return false;
        }

        @Override
        public Object get() throws InterruptedException, ExecutionException {
            return null;
        }

        @Override
        public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
            return null;
        }
    }
}

Test Execution Listener를 예로 들 수 있습니다.예:

public class SchedulerExecutionListener implements TestExecutionListener {

    @Override
    public void beforeTestClass(@NonNull TestContext testContext) {
        try {
            ScheduledAnnotationBeanPostProcessor schedulerProcessor = testContext.getApplicationContext().getBean(ScheduledAnnotationBeanPostProcessor.class);
            schedulerProcessor.destroy();
    } catch (Exception ignored) {
            ignored.printStackTrace();
            System.out.println(ignored.getMessage());
        }
}

그런 다음 testClass에 추가합니다.

@TestExecutionListeners(listeners = SchedulerExecutionListener .class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
class Test {}

이 문제를 해결하려면 테스트프로파일의 @EnableScheduling 설정을 디세블로 합니다.

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.EnableScheduling;

@Configuration
@Profile({"!test"})
@EnableScheduling
public class SchedulingConfiguration {
}

언급URL : https://stackoverflow.com/questions/29014496/disable-enablescheduling-on-spring-tests

반응형