Imaginez une situation où votre projet doit être compilé dans divers environnements.
Imaginez maintenant que tous les tests ne devraient pas réussir dans ces environnements - chacun a son propre ensemble de tests.
Et il est préférable de configurer le choix des tests à effectuer dans le fichier ... application.properties
- chaque test a son propre interrupteur marche / arrêt.
Sonne bien, non?
Alors bienvenue au chat, où nous implémentons tout cela en utilisant SpringBoot 2 et JUnit 5.
Presets
Tout d'abord, désactivons JUnit 4, qui vient par défaut dans SpringBoot 2, et activons JUnit 5 .
Pour ce faire, apportez des modifications à pom.xml
:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.3.2</version> <scope>test</scope> </dependency> </dependencies>
Solution proposée
Nous voulons annoter chaque test avec une annotation simple avec une propriété indiquant si le test est activé ou non. Permettez-moi de vous rappeler que nous allons stocker les valeurs de cette propriété dans le fichier application.properties
.
Annotation
Créez une annotation :
@Retention(RetentionPolicy.RUNTIME) @ExtendWith(TestEnabledCondition.class) public @interface TestEnabled { String property(); }
Traitement des annotations
Un gestionnaire d'annotations est indispensable.
public class TestEnabledCondition implements ExecutionCondition { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { Optional<TestEnabled> annotation = context.getElement().map(e -> e.getAnnotation(TestEnabled.class)); return context.getElement() .map(e -> e.getAnnotation(TestEnabled.class)) .map(annotation -> { String property = annotation.property(); return Optional.ofNullable(environment.getProperty(property, Boolean.class)) .map(value -> { if (Boolean.TRUE.equals(value)) { return ConditionEvaluationResult.enabled("Enabled by property: "+property); } else { return ConditionEvaluationResult.disabled("Disabled by property: "+property); } }).orElse( ConditionEvaluationResult.disabled("Disabled - property <"+property+"> not set!") ); }).orElse( ConditionEvaluationResult.enabled("Enabled by default") ); } }
Vous devez créer une classe (sans l'annotation Spring @Component
) qui implémente l'interface ExecutionCondition
.
Dans cette classe, vous devez implémenter une méthode de cette interface - ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context)
.
Cette méthode prend le contexte du test exécuté par JUnit et renvoie la condition selon laquelle le test doit être exécuté ou non.
En savoir plus sur l' exécution conditionnelle des tests JUnit5 dans la documentation officielle.
Mais comment vérifier la valeur de la propriété enregistrée dans application.properties
dans ce cas?
Accès au contexte Spring à partir du contexte JUnit
De cette façon, nous pouvons obtenir l'environnement Spring avec lequel notre test JUnit a été exécuté à partir d' ExtensionContext
.
Environment environment = SpringExtension.getApplicationContext(context).getEnvironment();
Vous pouvez jeter un œil au code complet de la classe TestEnabledCondition .
Créer des tests
Créons quelques tests et essayons de contrôler leur lancement:
@SpringBootTest public class SkiptestApplicationTests { @TestEnabled(property = "app.skip.test.first") @Test public void testFirst() { assertTrue(true); } @TestEnabled(property = "app.skip.test.second") @Test public void testSecond() { assertTrue(false); } }
Notre fichier application.properties
ressemble à ceci:
app.skip.test.first=true app.skip.test.second=false
Alors ...
Résultat de démarrage:

L'étape suivante consiste à séparer les préfixes de nos propriétés dans l'annotation de classe
L'écriture des noms de propriété complets depuis application.properties
avant chaque test est une tâche fastidieuse. Par conséquent, il est raisonnable de placer leur préfixe au niveau de la classe de test - dans une annotation distincte.
Créer une annotation pour stocker des préfixes - TestEnabledPrefix
:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestEnabledPrefix { String prefix(); }
Traitement et utilisation de l'annotation TestEnabledPrefix
Nous procédons au traitement de la nouvelle annotation.
Créons une classe d'assistance AnnotationDescription
Avec cette classe, nous pouvons stocker le nom de la propriété à partir de application.properties
et sa valeur.
public class TestEnabledCondition implements ExecutionCondition { static class AnnotationDescription { String name; Boolean annotationEnabled; AnnotationDescription(String prefix, String property) { this.name = prefix + property; } String getName() { return name; } AnnotationDescription setAnnotationEnabled(Boolean value) { this.annotationEnabled = value; return this; } Boolean isAnnotationEnabled() { return annotationEnabled; } } }
Cette classe nous est utile, car nous allons utiliser des expressions lambda.
Créons une méthode qui extraira la valeur de la propriété prefix de l'annotation de la classe TestEnabledPrefix
public class TestEnabledCondition implements ExecutionCondition { private AnnotationDescription makeDescription(ExtensionContext context, String property) { String prefix = context.getTestClass() .map(cl -> cl.getAnnotation(TestEnabledPrefix.class)) .map(TestEnabledPrefix::prefix) .map(pref -> !pref.isEmpty() && !pref.endsWith(".") ? pref + "." : "") .orElse(""); return new AnnotationDescription(prefix, property); } }
Et maintenant, vérifions la valeur de la propriété de application.properties
par le nom spécifié dans l'annotation de test
public class TestEnabledCondition implements ExecutionCondition { @Override public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) { Environment environment = SpringExtension.getApplicationContext(context).getEnvironment(); return context.getElement() .map(e -> e.getAnnotation(TestEnabled.class)) .map(TestEnabled::property) .map(property -> makeDescription(context, property)) .map(description -> description.setAnnotationEnabled(environment.getProperty(description.getName(), Boolean.class))) .map(description -> { if (description.isAnnotationEnabled()) { return ConditionEvaluationResult.enabled("Enabled by property: "+description.getName()); } else { return ConditionEvaluationResult.disabled("Disabled by property: "+description.getName()); } }).orElse( ConditionEvaluationResult.enabled("Enabled by default") ); } }
Le code complet de la classe est disponible ici.
Utilisation de la nouvelle annotation
Appliquez maintenant notre annotation à la classe de test :
@SpringBootTest @TestEnabledPrefix(property = "app.skip.test") public class SkiptestApplicationTests { @TestEnabled(property = "first") @Test public void testFirst() { assertTrue(true); } @TestEnabled(property = "second") @Test public void testSecond() { assertTrue(false); } }
Maintenant, notre code de test est plus propre et plus simple.
Je remercie les utilisateurs de reddit pour leurs conseils:
1) dpash pour des conseils
2) BoyRobot777 pour des conseils
PS
L'article est une traduction. La version anglaise est publiée dans le fichier README.md à côté du code de projet.