From 634d1c03a3d4b55b44c23c0b240a786331f12659 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 31 Aug 2016 16:11:58 +0200 Subject: [PATCH] Introduce @EnabledIf support for JUnit Jupiter This commit picks up where SPR-14614 left off by introducing a new @EnabledIf annotation to serve as a logical companion to @DisabledIf. In addition, this commit extracts common logic from DisabledIfCondition into a new AbstractExpressionEvaluatingCondition base class which the new EnabledIfCondition also extends. An @EnabledOnMac annotation is also included in the Javadoc as well as in the test suite to demonstrate support for custom composed annotations. Issue: SPR-14644 --- ...AbstractExpressionEvaluatingCondition.java | 167 ++++++++++++++++++ .../context/junit/jupiter/DisabledIf.java | 1 + .../junit/jupiter/DisabledIfCondition.java | 92 +--------- .../test/context/junit/jupiter/EnabledIf.java | 111 ++++++++++++ .../junit/jupiter/EnabledIfCondition.java | 66 +++++++ .../jupiter/DisabledIfConditionTestCase.java | 7 +- .../junit/jupiter/DisabledIfTestCase.java | 32 ++-- .../junit/jupiter/EnabledIfTestCase.java | 154 ++++++++++++++++ .../context/junit/jupiter/EnabledOnMac.java | 38 ++++ 9 files changed, 563 insertions(+), 105 deletions(-) create mode 100644 spring-test/src/main/java/org/springframework/test/context/junit/jupiter/AbstractExpressionEvaluatingCondition.java create mode 100644 spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIf.java create mode 100644 spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIfCondition.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledIfTestCase.java create mode 100644 spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledOnMac.java diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/AbstractExpressionEvaluatingCondition.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/AbstractExpressionEvaluatingCondition.java new file mode 100644 index 0000000000..c8f7305c8f --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/AbstractExpressionEvaluatingCondition.java @@ -0,0 +1,167 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit.jupiter; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.util.Optional; +import java.util.function.Function; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ContainerExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestExecutionCondition; + +import org.springframework.beans.factory.config.BeanExpressionContext; +import org.springframework.beans.factory.config.BeanExpressionResolver; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Abstract base class for implementations of {@link ContainerExecutionCondition} + * and {@link TestExecutionCondition} that evaluate expressions configured via + * annotations to determine if a container or test is enabled. + * + *

Expressions can be any of the following. + * + *

+ * + * @author Sam Brannen + * @author Tadaya Tsuyukubo + * @since 5.0 + * @see EnabledIf + * @see DisabledIf + */ +abstract class AbstractExpressionEvaluatingCondition implements ContainerExecutionCondition, TestExecutionCondition { + + private static final Log logger = LogFactory.getLog(AbstractExpressionEvaluatingCondition.class); + + + /** + * Evaluate the expression configured via the supplied annotation type on + * the {@link AnnotatedElement} for the supplied {@link ExtensionContext}. + * + * @param annotationType the type of annotation to process + * @param expressionExtractor a function that extracts the expression from + * the annotation + * @param reasonExtractor a function that extracts the reason from the + * annotation + * @param enabledOnTrue indicates whether the returned {@code ConditionEvaluationResult} + * should be {@link ConditionEvaluationResult#enabled enabled} if the expression + * evaluates to {@code true} + * @param context the {@code ExtensionContext} + * @return {@link ConditionEvaluationResult#enabled enabled} if the container + * or test should be enabled; otherwise {@link ConditionEvaluationResult#disabled disabled} + */ + protected ConditionEvaluationResult evaluateAnnotation(Class annotationType, + Function expressionExtractor, Function reasonExtractor, boolean enabledOnTrue, + ExtensionContext context) { + + AnnotatedElement element = context.getElement().get(); + Optional annotation = findMergedAnnotation(element, annotationType); + + if (!annotation.isPresent()) { + String reason = String.format("%s is enabled since @%s is not present", element, + annotationType.getSimpleName()); + if (logger.isDebugEnabled()) { + logger.debug(reason); + } + return ConditionEvaluationResult.enabled(reason); + } + + // @formatter:off + String expression = annotation.map(expressionExtractor).map(String::trim).filter(StringUtils::hasLength) + .orElseThrow(() -> new IllegalStateException(String.format( + "The expression in @%s on [%s] must not be blank", annotationType.getSimpleName(), element))); + // @formatter:on + + boolean result = evaluateExpression(expression, annotationType, context); + + if (result) { + String adjective = (enabledOnTrue ? "enabled" : "disabled"); + String reason = annotation.map(reasonExtractor).filter(StringUtils::hasText).orElseGet( + () -> String.format("%s is %s because @%s(\"%s\") evaluated to true", element, adjective, + annotationType.getSimpleName(), expression)); + if (logger.isInfoEnabled()) { + logger.info(reason); + } + return (enabledOnTrue ? ConditionEvaluationResult.enabled(reason) + : ConditionEvaluationResult.disabled(reason)); + } + else { + String adjective = (enabledOnTrue ? "disabled" : "enabled"); + String reason = String.format("%s is %s because @%s(\"%s\") did not evaluate to true", + element, adjective, annotationType.getSimpleName(), expression); + if (logger.isDebugEnabled()) { + logger.debug(reason); + } + return (enabledOnTrue ? ConditionEvaluationResult.disabled(reason) + : ConditionEvaluationResult.enabled(reason)); + } + } + + private boolean evaluateExpression(String expression, Class annotationType, + ExtensionContext extensionContext) { + + ApplicationContext applicationContext = SpringExtension.getApplicationContext(extensionContext); + + if (!(applicationContext instanceof ConfigurableApplicationContext)) { + if (logger.isWarnEnabled()) { + String contextType = (applicationContext != null ? applicationContext.getClass().getName() : "null"); + logger.warn(String.format("@%s(\"%s\") could not be evaluated on [%s] since the test " + + "ApplicationContext [%s] is not a ConfigurableApplicationContext", + annotationType.getSimpleName(), expression, extensionContext.getElement(), contextType)); + } + return false; + } + + ConfigurableBeanFactory configurableBeanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); + BeanExpressionResolver expressionResolver = configurableBeanFactory.getBeanExpressionResolver(); + BeanExpressionContext beanExpressionContext = new BeanExpressionContext(configurableBeanFactory, null); + + Object result = expressionResolver.evaluate(configurableBeanFactory.resolveEmbeddedValue(expression), + beanExpressionContext); + + Assert.state((result instanceof Boolean || result instanceof String), + () -> String.format("@%s(\"%s\") must evaluate to a String or a Boolean, not %s", + annotationType.getSimpleName(), expression, (result != null ? result.getClass().getName() : "null"))); + + return (result instanceof Boolean && ((Boolean) result).booleanValue()) + || (result instanceof String && Boolean.parseBoolean((String) result)); + } + + private static Optional findMergedAnnotation(AnnotatedElement element, + Class annotationType) { + return Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(element, annotationType)); + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIf.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIf.java index e5da019572..47551ca2b8 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIf.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIf.java @@ -54,6 +54,7 @@ import org.springframework.core.annotation.AliasFor; * @author Tadaya Tsuyukubo * @since 5.0 * @see SpringExtension + * @see EnabledIf * @see org.junit.jupiter.api.Disabled */ @Target({ ElementType.TYPE, ElementType.METHOD }) diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIfCondition.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIfCondition.java index cff88c74d6..8185346574 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIfCondition.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/DisabledIfCondition.java @@ -16,13 +16,6 @@ package org.springframework.test.context.junit.jupiter; -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.util.Optional; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.junit.jupiter.api.extension.ConditionEvaluationResult; import org.junit.jupiter.api.extension.ContainerExecutionCondition; import org.junit.jupiter.api.extension.ContainerExtensionContext; @@ -30,34 +23,24 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.TestExecutionCondition; import org.junit.jupiter.api.extension.TestExtensionContext; -import org.springframework.beans.factory.config.BeanExpressionContext; -import org.springframework.beans.factory.config.BeanExpressionResolver; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - /** * {@code DisabledIfCondition} is a composite {@link ContainerExecutionCondition} * and {@link TestExecutionCondition} that supports the {@link DisabledIf @DisabledIf} * annotation when using the Spring TestContext Framework in conjunction * with JUnit 5's Jupiter programming model. * - *

Any attempt to use {@code DisabledIfCondition} without the presence of - * {@link DisabledIf @DisabledIf} will result in an {@link IllegalStateException}. + *

Any attempt to use the {@code DisabledIfCondition} without the presence of + * {@link DisabledIf @DisabledIf} will result in an enabled + * {@link ConditionEvaluationResult}. * * @author Sam Brannen * @author Tadaya Tsuyukubo * @since 5.0 - * @see org.springframework.test.context.junit.jupiter.DisabledIf - * @see org.springframework.test.context.junit.jupiter.SpringExtension + * @see DisabledIf + * @see EnabledIf + * @see SpringExtension */ -public class DisabledIfCondition implements ContainerExecutionCondition, TestExecutionCondition { - - private static final Log logger = LogFactory.getLog(DisabledIfCondition.class); - +public class DisabledIfCondition extends AbstractExpressionEvaluatingCondition { /** * Containers are disabled if {@code @DisabledIf} is present on the test class @@ -77,65 +60,8 @@ public class DisabledIfCondition implements ContainerExecutionCondition, TestExe return evaluateDisabledIf(context); } - private ConditionEvaluationResult evaluateDisabledIf(ExtensionContext extensionContext) { - AnnotatedElement element = extensionContext.getElement().get(); - Optional disabledIf = findMergedAnnotation(element, DisabledIf.class); - Assert.state(disabledIf.isPresent(), () -> "@DisabledIf must be present on " + element); - - // @formatter:off - String expression = disabledIf.map(DisabledIf::expression).map(String::trim).filter(StringUtils::hasLength) - .orElseThrow(() -> new IllegalStateException(String.format( - "The expression in @DisabledIf on [%s] must not be blank", element))); - // @formatter:on - - if (isDisabled(expression, extensionContext)) { - String reason = disabledIf.map(DisabledIf::reason).filter(StringUtils::hasText).orElseGet( - () -> String.format("%s is disabled because @DisabledIf(\"%s\") evaluated to true", element, - expression)); - logger.info(reason); - return ConditionEvaluationResult.disabled(reason); - } - else { - String reason = String.format("%s is enabled because @DisabledIf(\"%s\") did not evaluate to true", - element, expression); - logger.debug(reason); - return ConditionEvaluationResult.enabled(reason); - } - } - - private boolean isDisabled(String expression, ExtensionContext extensionContext) { - ApplicationContext applicationContext = SpringExtension.getApplicationContext(extensionContext); - - if (!(applicationContext instanceof ConfigurableApplicationContext)) { - if (logger.isWarnEnabled()) { - String contextType = (applicationContext != null ? applicationContext.getClass().getName() : "null"); - logger.warn(String.format("@DisabledIf(\"%s\") could not be evaluated on [%s] since the test " + - "ApplicationContext [%s] is not a ConfigurableApplicationContext", - expression, extensionContext.getElement(), contextType)); - } - return false; - } - - ConfigurableBeanFactory configurableBeanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory(); - BeanExpressionResolver expressionResolver = configurableBeanFactory.getBeanExpressionResolver(); - BeanExpressionContext beanExpressionContext = new BeanExpressionContext(configurableBeanFactory, null); - - Object result = expressionResolver.evaluate(configurableBeanFactory.resolveEmbeddedValue(expression), - beanExpressionContext); - - Assert.state((result instanceof Boolean || result instanceof String), () -> - String.format("@DisabledIf(\"%s\") must evaluate to a String or a Boolean, not %s", expression, - (result != null ? result.getClass().getName() : "null"))); - - boolean disabled = (result instanceof Boolean && ((Boolean) result).booleanValue()) || - (result instanceof String && Boolean.parseBoolean((String) result)); - - return disabled; - } - - private static Optional findMergedAnnotation(AnnotatedElement element, - Class annotationType) { - return Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(element, annotationType)); + private ConditionEvaluationResult evaluateDisabledIf(ExtensionContext context) { + return evaluateAnnotation(DisabledIf.class, DisabledIf::expression, DisabledIf::reason, false, context); } } diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIf.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIf.java new file mode 100644 index 0000000000..bd8602d07c --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIf.java @@ -0,0 +1,111 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit.jupiter; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.core.annotation.AliasFor; + +/** + * {@code @EnabledIf} is used to signal that the annotated test class or test + * method is enabled and should be executed if the supplied + * {@link #expression} evaluates to {@code true}. + * + *

When applied at the class level, all test methods within that class + * are automatically enabled by default as well. + * + *

For basic examples, see the Javadoc for {@link #expression}. + * + *

This annotation may be used as a meta-annotation to create + * custom composed annotations. For example, a custom + * {@code @EnabledOnMac} annotation can be created as follows. + * + *

+ * {@literal @}Target({ ElementType.TYPE, ElementType.METHOD })
+ * {@literal @}Retention(RetentionPolicy.RUNTIME)
+ * {@literal @}EnabledIf(
+ *     expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}",
+ *     reason = "Enabled on Mac OS"
+ * )
+ * public {@literal @}interface EnabledOnMac {}
+ * 
+ * + * @author Sam Brannen + * @since 5.0 + * @see SpringExtension + * @see DisabledIf + * @see org.junit.jupiter.api.Disabled + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@ExtendWith(EnabledIfCondition.class) +public @interface EnabledIf { + + /** + * Alias for {@link #expression}; only intended to be used if an + * explicit {@link #reason} is not provided. + * + * @see #expression + */ + @AliasFor("expression") + String value() default ""; + + /** + * The expression that will be evaluated to determine if the annotated test + * class or test method is enabled. + * + *

If the expression evaluates to {@link Boolean#TRUE} or a {@link String} + * equal to {@code "true"} (ignoring case), the test will be enabled. + * + *

Expressions can be any of the following. + * + *

+ * + *

Note, however, that a text literal which is not the result of + * dynamic resolution of a property placeholder is of zero practical value + * since {@code @EnabledIf("false")} is equivalent to {@code @Disabled} + * and {@code @EnabledIf("true")} is logically meaningless. + * + * @see #reason + * @see #value + */ + @AliasFor("value") + String expression() default ""; + + /** + * The reason this test is enabled. + * + * @see #expression + */ + String reason() default ""; + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIfCondition.java b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIfCondition.java new file mode 100644 index 0000000000..aaba6b1f7a --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit/jupiter/EnabledIfCondition.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit.jupiter; + +import org.junit.jupiter.api.extension.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ContainerExecutionCondition; +import org.junit.jupiter.api.extension.ContainerExtensionContext; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestExecutionCondition; +import org.junit.jupiter.api.extension.TestExtensionContext; + +/** + * {@code EnabledIfCondition} is a composite {@link ContainerExecutionCondition} + * and {@link TestExecutionCondition} that supports the {@link EnabledIf @EnabledIf} + * annotation when using the Spring TestContext Framework in conjunction + * with JUnit 5's Jupiter programming model. + * + *

Any attempt to use the {@code EnabledIfCondition} without the presence of + * {@link EnabledIf @EnabledIf} will result in an enabled + * {@link ConditionEvaluationResult}. + * + * @author Sam Brannen + * @since 5.0 + * @see EnabledIf + * @see DisabledIf + * @see SpringExtension + */ +public class EnabledIfCondition extends AbstractExpressionEvaluatingCondition { + + /** + * Containers are enabled if {@code @EnabledIf} is present on the test class + * and the configured expression evaluates to {@code true}. + */ + @Override + public ConditionEvaluationResult evaluate(ContainerExtensionContext context) { + return evaluateEnabledIf(context); + } + + /** + * Tests are enabled if {@code @EnabledIf} is present on the test method + * and the configured expression evaluates to {@code true}. + */ + @Override + public ConditionEvaluationResult evaluate(TestExtensionContext context) { + return evaluateEnabledIf(context); + } + + private ConditionEvaluationResult evaluateEnabledIf(ExtensionContext context) { + return evaluateAnnotation(EnabledIf.class, EnabledIf::expression, EnabledIf::reason, true, context); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfConditionTestCase.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfConditionTestCase.java index 617a1f8747..1104215e17 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfConditionTestCase.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfConditionTestCase.java @@ -34,7 +34,6 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.endsWith; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -61,10 +60,8 @@ class DisabledIfConditionTestCase { @Test void missingDisabledIf() { - IllegalStateException exception = expectThrows(IllegalStateException.class, - () -> condition.evaluate(buildExtensionContext("missingDisabledIf"))); - - assertThat(exception.getMessage(), startsWith("@DisabledIf must be present")); + assertResult(condition.evaluate(buildExtensionContext("missingDisabledIf")), false, + endsWith("missingDisabledIf() is enabled since @DisabledIf is not present")); } @Test diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfTestCase.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfTestCase.java index aee2a06cbf..0f7c9e1c18 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfTestCase.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/DisabledIfTestCase.java @@ -46,73 +46,73 @@ class DisabledIfTestCase { @Test @DisabledIf("true") - void disabledByStringTrue() { + void disabledIfWithStringTrue() { fail("This test must be disabled"); } @Test @DisabledIf(" true ") - void disabledByStringTrueWithSurroundingWhitespace() { + void disabledIfWithStringTrueWithSurroundingWhitespace() { fail("This test must be disabled"); } @Test @DisabledIf("TrUe") - void disabledByStringTrueIgnoreCase() { + void disabledIfWithStringTrueIgnoreCase() { fail("This test must be disabled"); } @Test @DisabledIf("${foo}") - void disabledByPropertyPlaceholder() { + void disabledIfWithPropertyPlaceholder() { fail("This test must be disabled"); } @Test @DisabledIf("\t${foo} ") - void disabledByPropertyPlaceholderWithSurroundingWhitespace() { + void disabledIfWithPropertyPlaceholderWithSurroundingWhitespace() { fail("This test must be disabled"); } @Test - @DisabledIf("#{T(java.lang.Boolean).TRUE}") - void disabledBySpelBoolean() { + @DisabledIf("#{T(Boolean).TRUE}") + void disabledIfWithSpelBoolean() { fail("This test must be disabled"); } @Test - @DisabledIf(" #{T(java.lang.Boolean).TRUE} ") - void disabledBySpelBooleanWithSurroundingWhitespace() { + @DisabledIf(" #{T(Boolean).TRUE} ") + void disabledIfWithSpelBooleanWithSurroundingWhitespace() { fail("This test must be disabled"); } @Test @DisabledIf("#{'tr' + 'ue'}") - void disabledBySpelStringConcatenation() { + void disabledIfWithSpelStringConcatenation() { fail("This test must be disabled"); } @Test @DisabledIf("#{6 * 7 == 42}") - void disabledBySpelMathematicalComparison() { + void disabledIfWithSpelArithmeticComparison() { fail("This test must be disabled"); } @Test @DisabledOnMac - void disabledBySpelOsCheckInCustomComposedAnnotation() { + void disabledIfWithSpelOsCheckInCustomComposedAnnotation() { assertFalse(System.getProperty("os.name").contains("Mac"), "This test must be disabled on Mac OS"); } @Test @DisabledIf("#{@booleanTrueBean}") - void disabledBySpelBooleanTrueBean() { + void disabledIfWithSpelBooleanTrueBean() { fail("This test must be disabled"); } @Test @DisabledIf("#{@stringTrueBean}") - void disabledBySpelStringTrueBean() { + void disabledIfWithSpelStringTrueBean() { fail("This test must be disabled"); } @@ -128,12 +128,10 @@ class DisabledIfTestCase { fail("This test must be disabled"); } - // Even though method level condition is not disabling test, class level condition - // should take precedence @Test @DisabledIf("false") void bar() { - fail("This test must be disabled"); + fail("This test must be disabled due to class-level condition"); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledIfTestCase.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledIfTestCase.java new file mode 100644 index 0000000000..13631320f6 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledIfTestCase.java @@ -0,0 +1,154 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit.jupiter; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.TestPropertySource; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * Integration tests which verify support for {@link EnabledIf @EnabledIf} + * in conjunction with the {@link SpringExtension} in a JUnit 5 (Jupiter) + * environment. + * + * @author Tadaya Tsuyukubo + * @author Sam Brannen + * @since 5.0 + * @see EnabledIfConditionTestCase + * @see EnabledIf + * @see SpringExtension + */ +class EnabledIfTestCase { + + @SpringJUnitConfig(Config.class) + @TestPropertySource(properties = "foo = false") + @Nested + class EnabledIfOnMethodTestCase { + + @Test + @EnabledIf("false") + void enabledIfWithStringFalse() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf(" false ") + void enabledIfWithStringFalseWithSurroundingWhitespace() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("FaLsE") + void enabledIfWithStringFalseIgnoreCase() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("${foo}") + void enabledIfWithPropertyPlaceholder() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("\t${foo} ") + void enabledIfWithPropertyPlaceholderWithSurroundingWhitespace() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("#{T(Boolean).FALSE}") + void enabledIfWithSpelBoolean() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf(" #{T(Boolean).FALSE} ") + void enabledIfWithSpelBooleanWithSurroundingWhitespace() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("#{'fal' + 'se'}") + void enabledIfWithSpelStringConcatenation() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("#{1 + 2 == 4}") + void enabledIfWithSpelArithmeticComparison() { + fail("This test must be disabled"); + } + + @Test + @EnabledOnMac + void enabledIfWithSpelOsCheckInCustomComposedAnnotation() { + String os = System.getProperty("os.name").toLowerCase(); + assertTrue(os.contains("mac"), "This test must be enabled on Mac OS"); + assertFalse(os.contains("win"), "This test must be disabled on Windows"); + } + + @Test + @EnabledIf("#{@booleanFalseBean}") + void enabledIfWithSpelBooleanFalseBean() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("#{@stringFalseBean}") + void enabledIfWithSpelStringFalseBean() { + fail("This test must be disabled"); + } + } + + @SpringJUnitConfig(Config.class) + @Nested + @EnabledIf("false") + class EnabledIfOnClassTestCase { + + @Test + void foo() { + fail("This test must be disabled"); + } + + @Test + @EnabledIf("true") + void bar() { + fail("This test must be disabled due to class-level condition"); + } + } + + @Configuration + static class Config { + + @Bean + Boolean booleanFalseBean() { + return Boolean.FALSE; + } + + @Bean + String stringFalseBean() { + return "false"; + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledOnMac.java b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledOnMac.java new file mode 100644 index 0000000000..270d463466 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit/jupiter/EnabledOnMac.java @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.context.junit.jupiter; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Demo composed annotation for {@link EnabledIf @EnabledIf} that + * enables a test class or test method if the current operating system is + * Mac OS. + * + * @author Sam Brannen + * @since 5.0 + */ +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@EnabledIf(expression = "#{systemProperties['os.name'].toLowerCase().contains('mac')}", reason = "Enabled on Mac OS") +public @interface EnabledOnMac { +}