使用application.properties定制JUnit5测试的组成

设想一下您的项目需要在各种环境中进行编译的情况。


现在,假设并非所有测试都应在这些环境中通过-每个测试都有其自己的一组测试。


而且,最好在... application.properties文件中配置应执行哪些测试的选择-每个测试都有其自己的on / off开关。


听起来不错,不是吗?


然后欢迎来到猫,我们在这里使用SpringBoot 2和JUnit 5实现所有这些。


预设值


首先,让我们关闭SpringBoot 2中默认提供的JUnit 4,然后打开JUnit 5


为此,请对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> 

拟议的解决方案


我们想用一个简单的注释来注释每个测试,该注释带有一个属性,该属性指示是否启用该测试。 让我提醒您,我们将把此属性的值存储在application.properties文件中。


注解


创建一个注释


 @Retention(RetentionPolicy.RUNTIME) @ExtendWith(TestEnabledCondition.class) public @interface TestEnabled { String property(); } 

批注处理


注释处理程序是必不可少的。


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

您必须创建一个实现ExecutionCondition接口的类(不带Spring的批注@Component )。


在此类中,您需要实现此接口的一种方法ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context)


此方法获取JUnit运行的测试的上下文,并返回条件是否应该运行测试。


在官方文档中阅读有关有条件执行JUnit5测试的更多信息。


但是在这种情况下,我们如何检查在application.properties中注册的属性的值?


从JUnit上下文访问Spring上下文


这样,我们可以从ExtensionContext获得运行JUnit测试所使用的Spring环境。


 Environment environment = SpringExtension.getApplicationContext(context).getEnvironment(); 

您可以查看TestEnabledCondition类完整代码


创建测试


让我们创建一些测试并尝试控制其启动:


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

我们的application.properties文件如下所示:


 app.skip.test.first=true app.skip.test.second=false 

所以...


启动结果:



下一步是将属性的前缀分成类注释


在每次测试之前从application.properties编写完整的属性名称是一项繁琐的任务。 因此,将它们的前缀放在测试类的级别上是合理的-在单独的注释中。


创建用于存储前缀的注释 TestEnabledPrefix


 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface TestEnabledPrefix { String prefix(); } 

处理和使用TestEnabledPrefix批注


我们继续处理新注释。


让我们创建一个AnnotationDescription助手类


使用此类,我们可以存储application.properties的属性名称及其值。


 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; } } /* ... */ } 

此类对我们很有用,因为 我们将使用lambda表达式。


让我们创建一个方法,该方法将从TestEnabledPrefix类的注释中提取prefix属性的值


 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); } /* ... */ } 

现在,让我们通过测试注释中指定的名称来检查application.properties中的属性值


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


完整的类代码可在此处获得。


使用新的注释


现在将我们的注释应用于测试类


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

现在我们的测试代码更加简洁。


我要感谢reddit用户的建议:


1) dpash寻求建议
2) BoyRobot777寻求建议


聚苯乙烯


这篇文章是翻译。 英文版发布在项目代码旁边的README.md文件中。

Source: https://habr.com/ru/post/zh-CN464881/


All Articles