Lorsque j'exécute mes tests unitaires, il appelle mes tâches planifiées. Je souhaite empêcher ce comportement, qui est dû au fait que j'ai @EnableScheduling
sur la configuration principale de mon application.
Comment puis-je désactiver cela sur mes tests unitaires?
Je suis tombé sur ce question/réponse qui suggère la mise en place de profils?
Vous ne savez pas comment j'y arriverais? ou si c'est une exagération? Je pensais avoir une AppConfiguration distincte pour mes tests unitaires mais j'ai l'impression de répéter deux fois le code quand je fais ça?
@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
}
Exemple de test unitaire.
@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);
}
}
Si vous ne souhaitez pas utiliser de profils, vous pouvez ajouter un indicateur qui activera/désactivera la planification pour l'application
Dans votre AppConfiguration
ajoutez ceci
@ConditionalOnProperty(
value = "app.scheduling.enable", havingValue = "true", matchIfMissing = true
)
@Configuration
@EnableScheduling
public static class SchedulingConfiguration {
}
et dans votre test, ajoutez simplement cette annotation pour désactiver la planification
@TestPropertySource(properties = "app.scheduling.enable=false")
Une alternative serait de désenregistrer le post-processeur de bean qui planifie les événements. Cela peut être fait en plaçant simplement la classe suivante sur le chemin de classe de vos tests:
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);
}
}
}
Bien que cela soit assez simple et semble faire le travail, sachez que je n'ai pas beaucoup testé cela ou vérifié les implications possibles de la suppression d'un bean défini du registre ou de m'assurer que la commande de PostProcessors ne sera pas un problème ...
Je viens de paramétrer mon annotation @Scheduled avec des temps de retard configurables:
@Scheduled(fixedRateString = "${timing.updateData}", initialDelayString = "${timing.initialDelay}")
Dans mon test application.yaml:
timing:
updateData: 60000
initialDelay: 10000000000
Et application.yaml principale:
timing:
updateData: 60000
initialDelay: 1
Il ne s'agit pas de l'éteindre mais de créer un délai si long, les tests seront terminés avant son exécution. Pas la solution la plus élégante mais certainement l'une des plus faciles que j'ai trouvées.
Dans chaque test, vous définissez la configuration de ressort à utiliser, vous avez actuellement:
@ContextConfiguration(classes={AppConfiguration.class})
La pratique courante consiste à définir une configuration de ressort distincte pour votre application normale et pour vos tests.
AppConfiguration.Java
TestConfiguration.Java
Ensuite, dans votre test, vous référencez simplement TestConfiguration
à la place de votre AppConfiguration
actuel en utilisant @ContextConfiguration(classes={TestConfiguration.class})
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={TestConfiguration.class})
@Transactional
@TransactionConfiguration(defaultRollback = true)
@WebAppConfiguration
public class ExampleDaoTest
De cette façon, vous pouvez configurer n'importe quel paramètre pour vos tests différemment du code de production. Vous pouvez par exemple utiliser une base de données en mémoire pour vos tests au lieu d'une base de données régulière et bien plus encore.
J'ai pu résoudre ce problème en créant une méthode qui supprime les tâches planifiées lors des tests unitaires. Voici un exemple:
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();
}
}
Exemple d'utilisation:
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
}
J'espère que cela t'aides.