在测试中使用@EnabledIf和Spring环境中的spring.profiles.active属性

20
根据文档(https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/junit/jupiter/EnabledIf.html#expression--),您可以在测试类或测试方法上使用@EnabledIf注释,例如: @EnabledIf("${smoke.tests.enabled}") 或者 @EnabledIf("#{systemProperties['os.name'].toLowerCase().contains('mac')}") 其中字符串是Spring Environment中可用的属性的占位符。
假设我有以下application.yml文件:
smoke:
  tests:
    enabled: true
spring:
  profiles:
    active: test

并且以下是测试类:

@EnabledIf(value = "#{${spring.profiles.active} == 'test'}", loadContext = true)
@SpringBootTest
public class SomeClassForTests {

    @Autowired SomeType autowiredType;

    @Test
    public void someTest() {
        // test logic...
    }

运行测试时,我收到以下错误:

Test ignored.

org.junit.jupiter.engine.execution.ConditionEvaluationException: Failed to evaluate condition [org.springframework.test.context.junit.jupiter.EnabledIfCondition]: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'test' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?

    at org.junit.jupiter.engine.execution.ConditionEvaluator.evaluationException(ConditionEvaluator.java:91)
    at org.junit.jupiter.engine.execution.ConditionEvaluator.evaluate(ConditionEvaluator.java:80)
    at org.junit.jupiter.engine.execution.ConditionEvaluator.lambda$evaluate$2(ConditionEvaluator.java:66)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
    at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.tryAdvance(StreamSpliterators.java:302)
    at java.base/java.util.stream.Streams$ConcatSpliterator.tryAdvance(Streams.java:723)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:543)
    at org.junit.jupiter.engine.execution.ConditionEvaluator.evaluate(ConditionEvaluator.java:68)
    at org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.shouldBeSkipped(JupiterTestDescriptor.java:182)
    at org.junit.jupiter.engine.descriptor.JupiterTestDescriptor.shouldBeSkipped(JupiterTestDescriptor.java:54)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$checkWhetherSkipped$1(NodeTestTask.java:91)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.checkWhetherSkipped(NodeTestTask.java:91)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$4(NodeTestTask.java:112)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:98)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:74)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'test' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
    at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:164)
    at org.springframework.test.context.junit.jupiter.AbstractExpressionEvaluatingCondition.evaluateExpression(AbstractExpressionEvaluatingCondition.java:164)
    at org.springframework.test.context.junit.jupiter.AbstractExpressionEvaluatingCondition.evaluateAnnotation(AbstractExpressionEvaluatingCondition.java:107)
    at org.springframework.test.context.junit.jupiter.EnabledIfCondition.evaluateExecutionCondition(EnabledIfCondition.java:46)
    at org.junit.jupiter.engine.execution.ConditionEvaluator.evaluate(ConditionEvaluator.java:75)
    ... 37 more
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'test' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:217)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104)
    at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:91)
    at org.springframework.expression.spel.ast.OpEQ.getValueInternal(OpEQ.java:42)
    at org.springframework.expression.spel.ast.OpEQ.getValueInternal(OpEQ.java:32)
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:109)
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:265)
    at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:161)
    ... 41 more

我对Spring表达式语言并不太熟悉。我到底做错了什么?


更新1:

使用以下两个版本的注释:

@EnabledIf(value = "${spring.profiles.active == 'test'}", loadContext = true)

或者

@EnabledIf(value = "#{spring.profiles.active == 'test'}", loadContext = true)

会导致以下错误: org.junit.jupiter.engine.execution.ConditionEvaluationException: Failed to evaluate condition [org.springframework.test.context.junit.jupiter.EnabledIfCondition]: @EnabledIf("${spring.profiles.active == 'test'}") on class SomeClassForTests must evaluate to "true" or "false", not "${spring.profiles.active == 'test'}"


更新2:

以下内容:

@EnabledIf(value = "${spring.profiles.active} == 'test'", loadContext = true)

会导致以下错误:

org.junit.jupiter.engine.execution.ConditionEvaluationException: Failed to evaluate condition [org.springframework.test.context.junit.jupiter.EnabledIfCondition]: @EnabledIf("${spring.profiles.active} == 'test'") on class SomeClassForTests must evaluate to "true" or "false", not "test == 'test'"


更新3:

以下答案:

@EnabledIf(expression = "#{environment['spring.profiles.active'] == 'test'}", loadContext = true)

适用于只有一个活动配置文件的情况。如果您有多个活动配置文件,例如下面的情况,该怎么办?

smoke:
  tests:
    enabled: true
spring:
  profiles:
    active: 
    - test
    - someotherprofile

请查看我是如何解决这个问题的:https://dev59.com/1bHma4cB1Zd3GeqPGyJG#71719806 - GtdDev
请查看我在以下链接中的解决方案:https://dev59.com/1bHma4cB1Zd3GeqPGyJG#71719806 - GtdDev
3个回答

26

要检查Spring Environment中属性的确切值,您应该使用以下方法。

@EnabledIf(expression = "#{environment['spring.profiles.active'] == 'test'}", loadContext = true)

要检查哪些配置文件当前在Spring Environment中处于活动状态,您应该使用以下方法。

@EnabledIf(expression = "#{environment.acceptsProfiles('test', 'someotherprofile')}", loadContext = true)

谢谢,这对于只有一个活动配置文件的情况下非常有效。但是如果您有许多活动配置文件,您将如何使用Spring表达式来检查您关心的配置文件是否在活动配置文件列表中?(我已经更新了问题,附加了更多细节) - vab2048
1
我已更新我的答案,展示如何检查多个活动配置文件。 - Sam Brannen
@SamBrannen 我们正在使用Zonky在嵌入式Postgres数据库上运行集成测试。这是通过在测试类上放置@AutoConfigureEmbeddedDatabase来配置的。使用loadContext=true,即使表达式求值为false,Zonky也始终会初始化。在某些活动配置文件无法启动Zonky(例如在容器内部)的情况下,这将成为问题,而我们希望根据特定的活动配置文件禁用测试。您能想到解决这个困境的方法吗? - Hein Blöd
@SamBrannen 我尝试在类级别上使用给定表达式的 @EnabledIf,但没有成功。无论我配置了多少个或哪些配置文件,测试都会被执行。我尝试了相同结果的 @DisabledIf。我的 Junit5 设置不是纯净的,因为一些依赖项使用带有 junit-vintage-engine 的 Junit4。如果您有任何想法如何基于 Spring 配置文件完全禁用测试类,那将是太棒了! - zyexal

4

在SpEL中使用时,请尝试添加额外的撇号来包装属性:

最初的回答

@EnabledIf(value = "#{'${spring.profiles.active}' == 'test'}", loadContext = true)

当我尝试使用自定义属性时,它最终对我有用了!

(注:Original Answer翻译成"最初的回答"是错误的)


1

Your expression should look like this:

@EnabledIf(value = "${spring.profiles.active == 'test'}", loadContext = true)

现在的错误是:org.junit.jupiter.engine.execution.ConditionEvaluationException: Failed to evaluate condition [org.springframework.test.context.junit.jupiter.EnabledIfCondition]: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'spring' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid? - vab2048
1
use $ sign instead of # - Maciej Kowalski
我已经在原问题中添加了更新,包括更多的注释和错误信息。不幸的是,使用$而不是#也不起作用。 - vab2048
请查看以下链接:https://dev59.com/1bHma4cB1Zd3GeqPGyJG#71719806 - GtdDev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接