Stellen Sie sich eine Situation vor, in der Ihr Projekt in verschiedenen Umgebungen kompiliert werden muss.
Stellen Sie sich nun vor, dass nicht alle Tests in diesen Umgebungen bestanden werden sollten - jeder hat seine eigenen Tests.
Außerdem ist es vorzuziehen, die Auswahl der Tests in der Datei ... application.properties
zu konfigurieren. Jeder Test verfügt über einen eigenen Ein- / Ausschalter.
Hört sich toll an, nicht wahr?
Dann willkommen bei der Katze, wo wir all dies mit SpringBoot 2 und JUnit 5 implementieren.
Voreinstellungen
Deaktivieren Sie zunächst JUnit 4, das in SpringBoot 2 standardmäßig enthalten ist, und JUnit 5 .
Nehmen Sie dazu Änderungen an 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>
Vorgeschlagene Lösung
Wir möchten jeden Test mit einer einfachen Anmerkung mit einer Eigenschaft versehen, die angibt, ob der Test aktiviert ist oder nicht. Ich möchte Sie daran erinnern, dass wir die Werte dieser Eigenschaft in der Datei application.properties
speichern werden.
Anmerkung
Erstellen Sie eine Anmerkung :
@Retention(RetentionPolicy.RUNTIME) @ExtendWith(TestEnabledCondition.class) public @interface TestEnabled { String property(); }
Anmerkungsverarbeitung
Ein Annotation Handler ist unverzichtbar.
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") ); } }
Sie müssen eine Klasse erstellen (ohne die Annotation @Component
Spring), die die ExecutionCondition
Schnittstelle implementiert.
In dieser Klasse müssen Sie eine Methode dieser Schnittstelle implementieren - ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context)
.
Diese Methode verwendet den Kontext des von JUnit ausgeführten Tests und gibt die Bedingung zurück, ob der Test ausgeführt werden soll oder nicht.
Weitere Informationen zur bedingten Ausführung von JUnit5-Tests finden Sie in der offiziellen Dokumentation.
Aber wie überprüfen wir in diesem Fall den Wert der Eigenschaft, die in application.properties
registriert ist?
Zugriff auf den Spring-Kontext aus dem JUnit-Kontext
Auf diese Weise können wir die Spring-Umgebung, mit der unser JUnit-Test ausgeführt wurde, von ExtensionContext
.
Environment environment = SpringExtension.getApplicationContext(context).getEnvironment();
Sie können sich den vollständigen Code der TestEnabledCondition-Klasse ansehen .
Tests erstellen
Lassen Sie uns einige Tests erstellen und versuchen, deren Start zu kontrollieren:
@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); } }
Unsere Datei application.properties
sieht folgendermaßen aus:
app.skip.test.first=true app.skip.test.second=false
Also ...
Startergebnis:

Der nächste Schritt besteht darin, die Präfixe unserer Eigenschaften in die Klassenanmerkung zu trennen
Das Schreiben der vollständigen Eigenschaftsnamen aus application.properties
vor jedem Test ist eine mühsame Aufgabe. Daher ist es sinnvoll, das Präfix auf der Ebene der Testklasse zu platzieren - in einer separaten Anmerkung.
Erstellen Sie eine Anmerkung zum Speichern von Präfixen - TestEnabledPrefix
:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestEnabledPrefix { String prefix(); }
Verarbeiten und Verwenden der TestEnabledPrefix-Annotation
Wir fahren mit der Verarbeitung der neuen Anmerkung fort.
Erstellen wir eine AnnotationDescription-Hilfsklasse
Mit dieser Klasse können wir den Eigenschaftsnamen aus application.properties
und seinen Wert speichern.
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; } } }
Diese Klasse ist nützlich für uns, weil Wir werden Lambda-Ausdrücke verwenden.
Erstellen wir eine Methode, die den Wert der Präfix-Eigenschaft aus der Annotation der TestEnabledPrefix-Klasse extrahiert
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); } }
Und jetzt überprüfen wir den Wert der Eigenschaft aus application.properties
anhand des in der Testanmerkung angegebenen Namens
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") ); } }
Den vollständigen Klassencode finden Sie hier.
Verwenden der neuen Anmerkung
Wenden Sie nun unsere Anmerkung auf die Testklasse an :
@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); } }
Jetzt ist unser Testcode sauberer und einfacher.
Ich möchte den Nutzern von reddit für ihren Rat danken:
1) dpash um Rat
2) BoyRobot777 um Rat
PS
Der Artikel ist eine Übersetzung. Die englische Version wird in der Datei README.md neben dem Projektcode veröffentlicht.