diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java index 6524b65db4..72b300be0c 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractJUnit4SpringContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -18,6 +18,7 @@ package org.springframework.test.context.junit4; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.junit.runner.RunWith; import org.springframework.context.ApplicationContext; @@ -36,12 +37,12 @@ import org.springframework.test.context.web.ServletTestExecutionListener; * in a JUnit environment. * *

Concrete subclasses should typically declare a class-level - * {@link ContextConfiguration @ContextConfiguration} annotation to - * configure the {@link ApplicationContext application context} {@link + * {@link ContextConfiguration @ContextConfiguration} annotation to + * configure the {@linkplain ApplicationContext application context} {@link * ContextConfiguration#locations() resource locations} or {@link * ContextConfiguration#classes() annotated classes}. If your test does not - * need to load an application context, you may choose to omit the {@link - * ContextConfiguration @ContextConfiguration} declaration and to configure + * need to load an application context, you may choose to omit the + * {@link ContextConfiguration @ContextConfiguration} declaration and to configure * the appropriate {@link org.springframework.test.context.TestExecutionListener * TestExecutionListeners} manually. * @@ -54,12 +55,18 @@ import org.springframework.test.context.web.ServletTestExecutionListener; *

  • {@link org.springframework.test.context.support.DirtiesContextTestExecutionListener} * * - *

    Note: this class serves only as a convenience for extension. If you do not - * wish for your test classes to be tied to a Spring-specific class hierarchy, - * you may configure your own custom test classes by using - * {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration - * @ContextConfiguration}, {@link TestExecutionListeners - * @TestExecutionListeners}, etc. + *

    This class serves only as a convenience for extension. + *

    * *

    NOTE: As of Spring Framework 4.1, this class requires JUnit 4.9 or higher. * diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java index 6299311b13..01cbd861b8 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/AbstractTransactionalJUnit4SpringContextTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -60,13 +60,18 @@ import org.springframework.transaction.annotation.Transactional; *

  • {@link org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener} * * - *

    Note: this class serves only as a convenience for extension. If you do not - * wish for your test classes to be tied to a Spring-specific class hierarchy, - * you may configure your own custom test classes by using - * {@link SpringJUnit4ClassRunner}, {@link ContextConfiguration - * @ContextConfiguration}, {@link TestExecutionListeners - * @TestExecutionListeners}, {@link Transactional @Transactional}, - * etc. + *

    This class serves only as a convenience for extension. + *

    * *

    NOTE: As of Spring Framework 4.1, this class requires JUnit 4.9 or higher. * diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java index 547e43a29d..6762e37ca6 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java @@ -16,6 +16,7 @@ package org.springframework.test.context.junit4; +import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.commons.logging.Log; @@ -41,6 +42,8 @@ import org.springframework.test.annotation.ProfileValueUtils; import org.springframework.test.annotation.Repeat; import org.springframework.test.annotation.Timed; import org.springframework.test.context.TestContextManager; +import org.springframework.test.context.junit4.rules.SpringClassRule; +import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks; import org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks; import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks; @@ -72,6 +75,9 @@ import org.springframework.util.ReflectionUtils; *

  • {@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}
  • * * + *

    If you would like to use the Spring TestContext Framework with a runner + * other than this one, use {@link SpringClassRule} and {@link SpringMethodRule}. + * *

    NOTE: As of Spring Framework 4.1, this class requires JUnit 4.9 or higher. * * @author Sam Brannen @@ -80,6 +86,8 @@ import org.springframework.util.ReflectionUtils; * @see TestContextManager * @see AbstractJUnit4SpringContextTests * @see AbstractTransactionalJUnit4SpringContextTests + * @see org.springframework.test.context.junit4.rules.SpringClassRule + * @see org.springframework.test.context.junit4.rules.SpringMethodRule */ @SuppressWarnings("deprecation") public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { @@ -101,6 +109,19 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { private final TestContextManager testContextManager; + private static void ensureSpringRulesAreNotPresent(Class testClass) { + for (Field field : testClass.getFields()) { + if (SpringClassRule.class.isAssignableFrom(field.getType())) { + throw new IllegalStateException(String.format("Detected SpringClassRule field in test class [%s], but " + + "SpringClassRule cannot be used with the SpringJUnit4ClassRunner.", testClass.getName())); + } + if (SpringMethodRule.class.isAssignableFrom(field.getType())) { + throw new IllegalStateException(String.format("Detected SpringMethodRule field in test class [%s], " + + "but SpringMethodRule cannot be used with the SpringJUnit4ClassRunner.", testClass.getName())); + } + } + } + /** * Construct a new {@code SpringJUnit4ClassRunner} and initialize a * {@link TestContextManager} to provide Spring testing functionality to @@ -113,6 +134,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { if (logger.isDebugEnabled()) { logger.debug("SpringJUnit4ClassRunner constructor called with [" + clazz + "]."); } + ensureSpringRulesAreNotPresent(clazz); this.testContextManager = createTestContextManager(clazz); } @@ -166,7 +188,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { /** * Wrap the {@link Statement} returned by the parent implementation with a - * {@link RunBeforeTestClassCallbacks} statement, thus preserving the + * {@code RunBeforeTestClassCallbacks} statement, thus preserving the * default JUnit functionality while adding support for the Spring TestContext * Framework. * @see RunBeforeTestClassCallbacks @@ -179,7 +201,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { /** * Wrap the {@link Statement} returned by the parent implementation with a - * {@link RunAfterTestClassCallbacks} statement, thus preserving the default + * {@code RunAfterTestClassCallbacks} statement, thus preserving the default * JUnit functionality while adding support for the Spring TestContext Framework. * @see RunAfterTestClassCallbacks */ @@ -393,7 +415,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { /** * Wrap the {@link Statement} returned by the parent implementation with a - * {@link RunBeforeTestMethodCallbacks} statement, thus preserving the + * {@code RunBeforeTestMethodCallbacks} statement, thus preserving the * default functionality while adding support for the Spring TestContext * Framework. * @see RunBeforeTestMethodCallbacks @@ -407,7 +429,7 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { /** * Wrap the {@link Statement} returned by the parent implementation with a - * {@link RunAfterTestMethodCallbacks} statement, thus preserving the + * {@code RunAfterTestMethodCallbacks} statement, thus preserving the * default functionality while adding support for the Spring TestContext * Framework. * @see RunAfterTestMethodCallbacks @@ -423,10 +445,10 @@ public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { * Return a {@link Statement} that potentially repeats the execution of * the {@code next} statement. *

    Supports Spring's {@link Repeat @Repeat} annotation by returning a - * {@link SpringRepeat} statement initialized with the configured repeat + * {@code SpringRepeat} statement initialized with the configured repeat * count (if greater than {@code 1}); otherwise, the supplied statement * is returned unmodified. - * @return either a {@link SpringRepeat} or the supplied {@link Statement} + * @return either a {@code SpringRepeat} or the supplied {@code Statement} * as appropriate * @see SpringRepeat */ diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java new file mode 100644 index 0000000000..c57986cece --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringClassRule.java @@ -0,0 +1,209 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import org.springframework.test.context.TestContextManager; +import org.springframework.test.context.junit4.statements.ProfileValueChecker; +import org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks; +import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks; + +/** + * {@code SpringClassRule} is a custom JUnit {@link TestRule} that provides + * class-level functionality of the Spring TestContext Framework + * to standard JUnit tests by means of the {@link TestContextManager} and associated + * support classes and annotations. + * + *

    In contrast to the {@link org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * SpringJUnit4ClassRunner}, Spring's rule-based JUnit support has the advantage + * that it is independent of any {@link org.junit.runner.Runner Runner} and + * can therefore be combined with existing alternative runners like JUnit's + * {@code Parameterized} or third-party runners such as the {@code MockitoJUnitRunner}. + * + *

    In order to achieve the same functionality as the {@code SpringJUnit4ClassRunner}, + * however, a {@code SpringClassRule} must be combined with a {@link SpringMethodRule}, + * since {@code SpringClassRule} only provides the class-level features of the + * {@code SpringJUnit4ClassRunner}. + * + *

    Example Usage

    + *
     public class ExampleSpringIntegrationTest {
    + *
    + *    @ClassRule
    + *    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
    + *
    + *    @Rule
    + *    public final SpringMethodRule springMethodRule = new SpringMethodRule(this);
    + *
    + *    // ...
    + * }
    + * + *

    The following list constitutes all annotations currently supported directly + * or indirectly by {@code SpringClassRule}. (Note that additional annotations + * may be supported by various + * {@link org.springframework.test.context.TestExecutionListener TestExecutionListener} or + * {@link org.springframework.test.context.TestContextBootstrapper TestContextBootstrapper} + * implementations.) + * + *

    + * + *

    NOTE: This class requires JUnit 4.9 or higher. + * + * @author Sam Brannen + * @author Philippe Marschall + * @since 4.2 + * @see #apply(Statement, Description) + * @see SpringMethodRule + * @see org.springframework.test.context.TestContextManager + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + */ +public class SpringClassRule implements TestRule { + + private static final Log logger = LogFactory.getLog(SpringClassRule.class); + + /** + * This field is {@code volatile} since a {@code SpringMethodRule} can + * potentially access it from a different thread, depending on the type + * of JUnit runner in use. + */ + private volatile TestContextManager testContextManager; + + + /** + * Create a new {@link TestContextManager} for the supplied test class. + *

    Can be overridden by subclasses. + * @param clazz the test class to be managed + */ + protected TestContextManager createTestContextManager(Class clazz) { + return new TestContextManager(clazz); + } + + /** + * Get the {@link TestContextManager} associated with this rule. + *

    Will be {@code null} until the {@link #apply} method is invoked + * by a JUnit runner. + */ + protected final TestContextManager getTestContextManager() { + return this.testContextManager; + } + + /** + * Apply class-level functionality of the Spring TestContext + * Framework to the supplied {@code base} statement. + * + *

    Specifically, this method creates the {@link TestContextManager} used + * by this rule and its associated {@link SpringMethodRule} and invokes the + * {@link TestContextManager#beforeTestClass() beforeTestClass()} and + * {@link TestContextManager#afterTestClass() afterTestClass()} methods + * on the {@code TestContextManager}. + * + *

    In addition, this method checks whether the test is enabled in + * the current execution environment. This prevents classes with a + * non-matching {@code @IfProfileValue} annotation from running altogether, + * even skipping the execution of {@code beforeTestClass()} methods + * in {@code TestExecutionListeners}. + * + * @param base the base {@code Statement} that this rule should be applied to + * @param description a {@code Description} of the current test execution + * @return a statement that wraps the supplied {@code base} with class-level + * functionality of the Spring TestContext Framework + * @see #createTestContextManager + * @see #withBeforeTestClassCallbacks + * @see #withAfterTestClassCallbacks + * @see #withProfileValueCheck + */ + @Override + public Statement apply(final Statement base, final Description description) { + Class testClass = description.getTestClass(); + + if (logger.isDebugEnabled()) { + logger.debug("Applying SpringClassRule to test class [" + testClass.getName() + "]."); + } + + validateSpringMethodRuleConfiguration(testClass); + + this.testContextManager = createTestContextManager(testClass); + + Statement statement = base; + statement = withBeforeTestClassCallbacks(statement); + statement = withAfterTestClassCallbacks(statement); + statement = withProfileValueCheck(testClass, statement); + return statement; + } + + /** + * Wrap the supplied {@code statement} with a {@code RunBeforeTestClassCallbacks} statement. + * @see RunBeforeTestClassCallbacks + */ + protected Statement withBeforeTestClassCallbacks(Statement statement) { + return new RunBeforeTestClassCallbacks(statement, getTestContextManager()); + } + + /** + * Wrap the supplied {@code statement} with a {@code RunAfterTestClassCallbacks} statement. + * @see RunAfterTestClassCallbacks + */ + protected Statement withAfterTestClassCallbacks(Statement statement) { + return new RunAfterTestClassCallbacks(statement, getTestContextManager()); + } + + /** + * Wrap the supplied {@code statement} with a {@code ProfileValueChecker} statement. + * @see ProfileValueChecker + */ + protected Statement withProfileValueCheck(Class testClass, Statement statement) { + return new ProfileValueChecker(statement, testClass, null); + } + + private void validateSpringMethodRuleConfiguration(Class testClass) { + Field ruleField = null; + + for (Field field : testClass.getFields()) { + int modifiers = field.getModifiers(); + if (!Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers) + && (SpringMethodRule.class.isAssignableFrom(field.getType()))) { + ruleField = field; + break; + } + } + + if (ruleField == null) { + throw new IllegalStateException(String.format( + "Failed to find 'public SpringMethodRule' field in test class [%s]. " + + "Consult the Javadoc for SpringClassRule for details.", testClass.getName())); + } + + if (!ruleField.isAnnotationPresent(Rule.class)) { + throw new IllegalStateException(String.format( + "SpringMethodRule field [%s] must be annotated with JUnit's @Rule annotation. " + + "Consult the Javadoc for SpringClassRule for details.", ruleField)); + } + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java new file mode 100644 index 0000000000..7f9dbc1a64 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/SpringMethodRule.java @@ -0,0 +1,294 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import java.lang.reflect.Field; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.junit.ClassRule; +import org.junit.rules.MethodRule; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.Statement; + +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.test.annotation.Repeat; +import org.springframework.test.annotation.Timed; +import org.springframework.test.context.TestContextManager; +import org.springframework.test.context.junit4.statements.ProfileValueChecker; +import org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks; +import org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks; +import org.springframework.test.context.junit4.statements.RunPrepareTestInstanceCallbacks; +import org.springframework.test.context.junit4.statements.SpringFailOnTimeout; +import org.springframework.test.context.junit4.statements.SpringRepeat; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * {@code SpringMethodRule} is a custom JUnit {@link MethodRule} that + * provides instance-level and method-level functionality of the + * Spring TestContext Framework to standard JUnit tests by means + * of the {@link TestContextManager} and associated support classes and + * annotations. + * + *

    In contrast to the {@link org.springframework.test.context.junit4.SpringJUnit4ClassRunner + * SpringJUnit4ClassRunner}, Spring's rule-based JUnit support has the advantage + * that it is independent of any {@link org.junit.runner.Runner Runner} and + * can therefore be combined with existing alternative runners like JUnit's + * {@code Parameterized} or third-party runners such as the {@code MockitoJUnitRunner}. + * + *

    In order to achieve the same functionality as the {@code SpringJUnit4ClassRunner}, + * however, a {@code SpringMethodRule} must be combined with a {@link SpringClassRule}, + * since {@code SpringMethodRule} only provides the method-level features of the + * {@code SpringJUnit4ClassRunner}. + * + *

    Example Usage

    + *
     public class ExampleSpringIntegrationTest {
    + *
    + *    @ClassRule
    + *    public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
    + *
    + *    @Rule
    + *    public final SpringMethodRule springMethodRule = new SpringMethodRule(this);
    + *
    + *    // ...
    + * }
    + * + *

    The following list constitutes all annotations currently supported directly + * or indirectly by {@code SpringMethodRule}. (Note that additional annotations + * may be supported by various + * {@link org.springframework.test.context.TestExecutionListener TestExecutionListener} or + * {@link org.springframework.test.context.TestContextBootstrapper TestContextBootstrapper} + * implementations.) + * + *

    + * + *

    NOTE: This class requires JUnit 4.9 or higher. + * + * @author Sam Brannen + * @author Philippe Marschall + * @since 4.2 + * @see #apply(Statement, FrameworkMethod, Object) + * @see SpringClassRule + * @see org.springframework.test.context.TestContextManager + * @see org.springframework.test.context.junit4.SpringJUnit4ClassRunner + */ +public class SpringMethodRule implements MethodRule { + + private static final Log logger = LogFactory.getLog(SpringMethodRule.class); + + /** + * {@code SpringMethodRule} retains a reference to the {@code SpringClassRule} + * instead of the {@code TestContextManager}, since the class rule owns + * the {@code TestContextManager}. + */ + private final SpringClassRule springClassRule; + + + /** + * Construct a new {@code SpringMethodRule} for the supplied test instance. + * + *

    The test class must declare a {@code public static final SpringClassRule} + * field (i.e., a constant) that is annotated with JUnit's + * {@link ClassRule @ClassRule} — for example: + * + *

     @ClassRule
    +	 * public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
    + * + * @param testInstance the test instance, never {@code null} + * @throws IllegalStateException if the test class does not declare an + * appropriate {@code SpringClassRule} constant. + */ + public SpringMethodRule(Object testInstance) { + Assert.notNull(testInstance, "testInstance must not be null"); + this.springClassRule = retrieveAndValidateSpringClassRule(testInstance.getClass()); + } + + /** + * Apply instance-level and method-level functionality + * of the Spring TestContext Framework to the supplied {@code base} + * statement. + * + *

    Specifically, this method invokes the + * {@link TestContextManager#prepareTestInstance prepareTestInstance()}, + * {@link TestContextManager#beforeTestMethod beforeTestMethod()}, and + * {@link TestContextManager#afterTestMethod afterTestMethod()} methods + * on the {@code TestContextManager}, potentially with Spring timeouts + * and repetitions. + * + *

    In addition, this method checks whether the test is enabled in + * the current execution environment. This prevents methods with a + * non-matching {@code @IfProfileValue} annotation from running altogether, + * even skipping the execution of {@code prepareTestInstance()} methods + * in {@code TestExecutionListeners}. + * + * @param base the base {@code Statement} that this rule should be applied to + * @param frameworkMethod the method which is about to be invoked on the test instance + * @param testInstance the current test instance + * @return a statement that wraps the supplied {@code base} with instance-level + * and method-level functionality of the Spring TestContext Framework + * @see #withBeforeTestMethodCallbacks + * @see #withAfterTestMethodCallbacks + * @see #withPotentialRepeat + * @see #withPotentialTimeout + * @see #withTestInstancePreparation + * @see #withProfileValueCheck + */ + @Override + public Statement apply(final Statement base, final FrameworkMethod frameworkMethod, final Object testInstance) { + if (logger.isDebugEnabled()) { + logger.debug("Applying SpringMethodRule to test method [" + frameworkMethod.getMethod() + "]."); + } + + Statement statement = base; + statement = withBeforeTestMethodCallbacks(frameworkMethod, testInstance, statement); + statement = withAfterTestMethodCallbacks(frameworkMethod, testInstance, statement); + statement = withTestInstancePreparation(testInstance, statement); + statement = withPotentialRepeat(frameworkMethod, testInstance, statement); + statement = withPotentialTimeout(frameworkMethod, testInstance, statement); + statement = withProfileValueCheck(frameworkMethod, testInstance, statement); + return statement; + } + + /** + * Get the {@link TestContextManager} associated with this rule. + */ + protected final TestContextManager getTestContextManager() { + return this.springClassRule.getTestContextManager(); + } + + /** + * Wrap the supplied {@link Statement} with a {@code ProfileValueChecker} statement. + * @see ProfileValueChecker + */ + protected Statement withProfileValueCheck(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + return new ProfileValueChecker(statement, testInstance.getClass(), frameworkMethod.getMethod()); + } + + /** + * Wrap the supplied {@link Statement} with a {@code RunPrepareTestInstanceCallbacks} statement. + * @see RunPrepareTestInstanceCallbacks + */ + protected Statement withTestInstancePreparation(Object testInstance, Statement statement) { + return new RunPrepareTestInstanceCallbacks(statement, testInstance, getTestContextManager()); + } + + /** + * Wrap the supplied {@link Statement} with a {@code RunBeforeTestMethodCallbacks} statement. + * @see RunBeforeTestMethodCallbacks + */ + protected Statement withBeforeTestMethodCallbacks(FrameworkMethod frameworkMethod, Object testInstance, + Statement statement) { + return new RunBeforeTestMethodCallbacks(statement, testInstance, frameworkMethod.getMethod(), + getTestContextManager()); + } + + /** + * Wrap the supplied {@link Statement} with a {@code RunAfterTestMethodCallbacks} statement. + * @see RunAfterTestMethodCallbacks + */ + protected Statement withAfterTestMethodCallbacks(FrameworkMethod frameworkMethod, Object testInstance, + Statement statement) { + return new RunAfterTestMethodCallbacks(statement, testInstance, frameworkMethod.getMethod(), + getTestContextManager()); + } + + /** + * Return a {@link Statement} that potentially repeats the execution of + * the {@code next} statement. + *

    Supports Spring's {@link Repeat @Repeat} annotation by returning a + * {@link SpringRepeat} statement initialized with the configured repeat + * count (if greater than {@code 1}); otherwise, the supplied statement + * is returned unmodified. + * @return either a {@code SpringRepeat} or the supplied {@code Statement} + * @see SpringRepeat + */ + protected Statement withPotentialRepeat(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { + Repeat repeatAnnotation = AnnotationUtils.getAnnotation(frameworkMethod.getMethod(), Repeat.class); + int repeat = (repeatAnnotation != null ? repeatAnnotation.value() : 1); + return (repeat > 1 ? new SpringRepeat(next, frameworkMethod.getMethod(), repeat) : next); + } + + /** + * Return a {@link Statement} that potentially throws an exception if + * the {@code next} statement in the execution chain takes longer than + * a specified timeout. + *

    Supports Spring's {@link Timed @Timed} annotation by returning a + * {@link SpringFailOnTimeout} statement initialized with the configured + * timeout (if greater than {@code 0}); otherwise, the supplied statement + * is returned unmodified. + * @return either a {@code SpringFailOnTimeout} or the supplied {@code Statement} + * @see #getSpringTimeout(FrameworkMethod) + * @see SpringFailOnTimeout + */ + protected Statement withPotentialTimeout(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { + long springTimeout = getSpringTimeout(frameworkMethod); + return (springTimeout > 0 ? new SpringFailOnTimeout(next, springTimeout) : next); + } + + /** + * Retrieve the configured Spring-specific {@code timeout} from the + * {@link Timed @Timed} annotation on the supplied + * {@linkplain FrameworkMethod test method}. + * @return the timeout, or {@code 0} if none was specified + */ + protected long getSpringTimeout(FrameworkMethod frameworkMethod) { + AnnotationAttributes annAttrs = AnnotatedElementUtils.findAnnotationAttributes(frameworkMethod.getMethod(), + Timed.class.getName()); + if (annAttrs == null) { + return 0; + } + else { + long millis = annAttrs. getNumber("millis").longValue(); + return millis > 0 ? millis : 0; + } + } + + private static SpringClassRule retrieveAndValidateSpringClassRule(Class testClass) { + Field springClassRuleField = null; + + for (Field field : testClass.getFields()) { + if (ReflectionUtils.isPublicStaticFinal(field) && (SpringClassRule.class.isAssignableFrom(field.getType()))) { + springClassRuleField = field; + break; + } + } + + if (springClassRuleField == null) { + throw new IllegalStateException(String.format( + "Failed to find 'public static final SpringClassRule' field in test class [%s]. " + + "Consult the Javadoc for SpringClassRule for details.", testClass.getName())); + } + + if (!springClassRuleField.isAnnotationPresent(ClassRule.class)) { + throw new IllegalStateException(String.format( + "SpringClassRule field [%s] must be annotated with JUnit's @ClassRule annotation. " + + "Consult the Javadoc for SpringClassRule for details.", springClassRuleField)); + } + + return (SpringClassRule) ReflectionUtils.getField(springClassRuleField, null); + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/rules/package-info.java b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/package-info.java new file mode 100644 index 0000000000..16124c162d --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/rules/package-info.java @@ -0,0 +1,4 @@ +/** + * Custom JUnit {@code Rules} used in the Spring TestContext Framework. + */ +package org.springframework.test.context.junit4.rules; diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java new file mode 100644 index 0000000000..83861e44e6 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/ProfileValueChecker.java @@ -0,0 +1,105 @@ +/* + * Copyright 2002-2015 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.junit4.statements; + +import java.lang.reflect.Method; + +import org.junit.Assume; +import org.junit.runners.model.Statement; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.test.annotation.IfProfileValue; +import org.springframework.test.annotation.ProfileValueUtils; +import org.springframework.util.Assert; + +/** + * {@code ProfileValueChecker} is a custom JUnit {@link Statement} that checks + * whether a test class or test method is enabled in the current environment + * via Spring's {@link IfProfileValue @IfProfileValue} annotation. + * + * @author Sam Brannen + * @author Philippe Marschall + * @since 4.2 + * @see #evaluate() + * @see IfProfileValue + * @see ProfileValueUtils + */ +public class ProfileValueChecker extends Statement { + + private final Statement next; + + private final Class testClass; + + private final Method testMethod; + + + /** + * Construct a new {@code ProfileValueChecker} statement. + * + * @param next the next {@code Statement} in the execution chain; never + * {@code null} + * @param testClass the test class to check; never {@code null} + * @param testMethod the test method to check; may be {@code null} if + * this {@code ProfileValueChecker} is being applied at the class level + */ + public ProfileValueChecker(Statement next, Class testClass, Method testMethod) { + Assert.notNull(next, "The next statement must not be null"); + Assert.notNull(testClass, "The test class must not be null"); + this.next = next; + this.testClass = testClass; + this.testMethod = testMethod; + } + + /** + * Determine if the test specified by arguments to the + * {@linkplain #ProfileValueChecker constructor} is enabled in + * the current environment, as configured via the {@link IfProfileValue + * @IfProfileValue} annotation. + *

    If the test is not annotated with {@code @IfProfileValue} it is + * considered enabled. + *

    If a test is not enabled, this method will abort further evaluation + * of the execution chain with a failed assumption; otherwise, this method + * will simply evaluate the next {@link Statement} in the execution chain. + * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class) + * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Method, Class) + * @see org.junit.Assume + */ + @Override + public void evaluate() throws Throwable { + if (this.testMethod == null) { + if (!ProfileValueUtils.isTestEnabledInThisEnvironment(testClass)) { + // Invoke assumeTrue() with false to avoid direct reference to JUnit's + // AssumptionViolatedException which exists in two packages as of JUnit 4.12. + Assume.assumeTrue(String.format( + "Profile configured via [%s] is not enabled in this environment for test class [%s].", + AnnotationUtils.findAnnotation(testClass, IfProfileValue.class), testClass.getName()), false); + } + } + else { + if (!ProfileValueUtils.isTestEnabledInThisEnvironment(testMethod, testClass)) { + // Invoke assumeTrue() with false to avoid direct reference to JUnit's + // AssumptionViolatedException which exists in two packages as of JUnit 4.12. + Assume.assumeTrue(String.format( + "Profile configured via @IfProfileValue is not enabled in this environment for test method [%s].", + testMethod), false); + } + } + + this.next.evaluate(); + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java index f9e9261e3e..af2b699a2a 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestClassCallbacks.java @@ -25,13 +25,15 @@ import org.junit.runners.model.Statement; import org.springframework.test.context.TestContextManager; /** - * {@code RunAfterTestClassCallbacks} is a custom JUnit {@link Statement} which allows the - * Spring TestContext Framework to be plugged into the JUnit execution chain by - * calling {@link TestContextManager#afterTestClass() afterTestClass()} on the supplied + * {@code RunAfterTestClassCallbacks} is a custom JUnit {@link Statement} which allows + * the Spring TestContext Framework to be plugged into the JUnit execution chain + * by calling {@link TestContextManager#afterTestClass afterTestClass()} on the supplied * {@link TestContextManager}. * + *

    NOTE: This class requires JUnit 4.9 or higher. + * * @see #evaluate() - * @see RunBeforeTestMethodCallbacks + * @see RunBeforeTestClassCallbacks * @author Sam Brannen * @since 3.0 */ diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java index 68408b2746..b296b3a939 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunAfterTestMethodCallbacks.java @@ -28,8 +28,10 @@ import org.springframework.test.context.TestContextManager; /** * {@code RunAfterTestMethodCallbacks} is a custom JUnit {@link Statement} which allows * the Spring TestContext Framework to be plugged into the JUnit execution chain - * by calling {@link TestContextManager#afterTestMethod(Object, Method, Throwable) - * afterTestMethod()} on the supplied {@link TestContextManager}. + * by calling {@link TestContextManager#afterTestMethod afterTestMethod()} on the supplied + * {@link TestContextManager}. + * + *

    NOTE: This class requires JUnit 4.9 or higher. * * @see #evaluate() * @see RunBeforeTestMethodCallbacks diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunPrepareTestInstanceCallbacks.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunPrepareTestInstanceCallbacks.java new file mode 100644 index 0000000000..87c2824528 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/RunPrepareTestInstanceCallbacks.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2015 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.junit4.statements; + +import org.junit.runners.model.Statement; + +import org.springframework.test.context.TestContextManager; + +/** + * {@code RunPrepareTestInstanceCallbacks} is a custom JUnit {@link Statement} which + * allows the Spring TestContext Framework to be plugged into the JUnit + * execution chain by calling {@link TestContextManager#prepareTestInstance(Object) + * prepareTestInstance()} on the supplied {@link TestContextManager}. + * + * @see #evaluate() + * @author Sam Brannen + * @since 4.2 + */ +public class RunPrepareTestInstanceCallbacks extends Statement { + + private final Statement next; + + private final Object testInstance; + + private final TestContextManager testContextManager; + + + /** + * Construct a new {@code RunPrepareTestInstanceCallbacks} statement. + * + * @param next the next {@code Statement} in the execution chain; never {@code null} + * @param testInstance the current test instance; never {@code null} + * @param testContextManager the {@code TestContextManager} upon which to call + * {@code prepareTestInstance()}; never {@code null} + */ + public RunPrepareTestInstanceCallbacks(Statement next, Object testInstance, TestContextManager testContextManager) { + this.next = next; + this.testInstance = testInstance; + this.testContextManager = testContextManager; + } + + /** + * Invoke {@link TestContextManager#prepareTestInstance(Object)} and + * then evaluate the next {@link Statement} in the execution chain + * (typically an instance of {@link RunAfterTestMethodCallbacks}). + */ + @Override + public void evaluate() throws Throwable { + this.testContextManager.prepareTestInstance(testInstance); + this.next.evaluate(); + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java index def818bc98..b12646ab28 100644 --- a/spring-test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/statements/SpringFailOnTimeout.java @@ -28,6 +28,11 @@ import org.springframework.test.annotation.Timed; * if the next statement in the execution chain takes more than the specified * number of milliseconds. * + *

    In contrast to JUnit's + * {@link org.junit.internal.runners.statements.FailOnTimeout FailOnTimeout}, + * the next {@code statement} will be executed in the same thread as the + * caller and will therefore not be aborted preemptively. + * * @see #evaluate() * @author Sam Brannen * @since 3.0 @@ -53,11 +58,9 @@ public class SpringFailOnTimeout extends Statement { /** * Evaluate the next {@link Statement statement} in the execution chain - * (typically an instance of - * {@link org.junit.internal.runners.statements.InvokeMethod InvokeMethod} - * or {@link org.junit.internal.runners.statements.ExpectException - * ExpectException}) and throw a {@link TimeoutException} if the next - * {@code statement} executes longer than the specified {@code timeout}. + * (typically an instance of {@link SpringRepeat}) and throw a + * {@link TimeoutException} if the next {@code statement} executes longer + * than the specified {@code timeout}. */ @Override public void evaluate() throws Throwable { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/AbsolutePathSpringJUnit4ClassRunnerAppCtxTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/AbsolutePathSpringJUnit4ClassRunnerAppCtxTests.java index fc06f6b272..500ab69331 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/AbsolutePathSpringJUnit4ClassRunnerAppCtxTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/AbsolutePathSpringJUnit4ClassRunnerAppCtxTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -16,8 +16,6 @@ package org.springframework.test.context.junit4; -import org.junit.runner.RunWith; - import org.springframework.test.context.ContextConfiguration; /** @@ -31,7 +29,6 @@ import org.springframework.test.context.ContextConfiguration; * @see ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests * @see RelativePathSpringJUnit4ClassRunnerAppCtxTests */ -@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { SpringJUnit4ClassRunnerAppCtxTests.DEFAULT_CONTEXT_RESOURCE_PATH }, inheritLocations = false) public class AbsolutePathSpringJUnit4ClassRunnerAppCtxTests extends SpringJUnit4ClassRunnerAppCtxTests { /* all tests are in the parent class. */ diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java index 4b48cd2dbc..9309092107 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/BeforeAndAfterTransactionAnnotationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -19,10 +19,13 @@ package org.springframework.test.context.junit4; import javax.annotation.Resource; import javax.sql.DataSource; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.springframework.jdbc.core.JdbcTemplate; @@ -56,6 +59,9 @@ public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactio protected boolean inTransaction = false; + @Rule + public final TestName testName = new TestName(); + @BeforeClass public static void beforeClass() { @@ -94,10 +100,21 @@ public class BeforeAndAfterTransactionAnnotationTests extends AbstractTransactio @Before public void before() { + assertShouldBeInTransaction(); assertEquals("Verifying the number of rows in the person table before a test method.", (this.inTransaction ? 1 : 0), countRowsInPersonTable(jdbcTemplate)); } + private void assertShouldBeInTransaction() { + boolean shouldBeInTransaction = !testName.getMethodName().equals("nonTransactionalMethod"); + assertInTransaction(shouldBeInTransaction); + } + + @After + public void after() { + assertShouldBeInTransaction(); + } + @Test @Transactional public void transactionalMethod1() { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java index 4bb3fb5bfa..dfe209437c 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.java @@ -16,8 +16,6 @@ package org.springframework.test.context.junit4; -import org.junit.runner.RunWith; - import org.springframework.test.context.ContextConfiguration; import org.springframework.util.ResourceUtils; @@ -33,7 +31,6 @@ import org.springframework.util.ResourceUtils; * @see AbsolutePathSpringJUnit4ClassRunnerAppCtxTests * @see RelativePathSpringJUnit4ClassRunnerAppCtxTests */ -@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests.CLASSPATH_CONTEXT_RESOURCE_PATH }) public class ClassPathResourceSpringJUnit4ClassRunnerAppCtxTests extends SpringJUnit4ClassRunnerAppCtxTests { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java index 8452c3c1fc..b962ad1980 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ExpectedExceptionSpringRunnerTests.java @@ -43,23 +43,19 @@ public class ExpectedExceptionSpringRunnerTests { @Test public void expectedExceptions() throws Exception { - Class testClass = ExpectedExceptionSpringRunnerTestCase.class; + Class testClass = ExpectedExceptionSpringRunnerTestCase.class; TrackingRunListener listener = new TrackingRunListener(); RunNotifier notifier = new RunNotifier(); notifier.addListener(listener); new SpringJUnit4ClassRunner(testClass).run(notifier); - assertEquals("Verifying number of failures for test class [" + testClass + "].", 0, - listener.getTestFailureCount()); - assertEquals("Verifying number of tests started for test class [" + testClass + "].", 1, - listener.getTestStartedCount()); - assertEquals("Verifying number of tests finished for test class [" + testClass + "].", 1, - listener.getTestFinishedCount()); + assertEquals("failures for test class [" + testClass + "].", 0, listener.getTestFailureCount()); + assertEquals("tests started for test class [" + testClass + "].", 1, listener.getTestStartedCount()); + assertEquals("tests finished for test class [" + testClass + "].", 1, listener.getTestFinishedCount()); } @Ignore("TestCase classes are run manually by the enclosing test class") - @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({}) public static final class ExpectedExceptionSpringRunnerTestCase { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java index 779f929894..d227864159 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/FailingBeforeAndAfterMethodsJUnitTests.java @@ -16,16 +16,17 @@ package org.springframework.test.context.junit4; -import java.util.Arrays; -import java.util.Collection; +import java.lang.reflect.Constructor; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.springframework.beans.BeanUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; @@ -33,30 +34,26 @@ import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.support.AbstractTestExecutionListener; import org.springframework.test.context.transaction.AfterTransaction; import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ClassUtils; import static org.junit.Assert.*; /** - *

    * JUnit 4 based integration test for verifying that 'before' and 'after' * methods of {@link TestExecutionListener TestExecutionListeners} as well as * {@link BeforeTransaction @BeforeTransaction} and * {@link AfterTransaction @AfterTransaction} methods can fail a test in a - * JUnit 4.4 environment, as requested in SPR-3960. - *

    - *

    - * Indirectly, this class also verifies that all {@link TestExecutionListener} + * JUnit environment, as requested in + * SPR-3960. + * + *

    Indirectly, this class also verifies that all {@link TestExecutionListener} * lifecycle callbacks are called. - *

    - *

    - * As of Spring 3.0, this class also tests support for the new + * + *

    As of Spring 3.0, this class also tests support for the new * {@link TestExecutionListener#beforeTestClass(TestContext) beforeTestClass()} * and {@link TestExecutionListener#afterTestClass(TestContext) * afterTestClass()} lifecycle callback methods. - *

    * * @author Sam Brannen * @since 2.5 @@ -68,38 +65,42 @@ public class FailingBeforeAndAfterMethodsJUnitTests { @Parameters(name = "{0}") - public static Collection testData() { - return Arrays.asList(new Object[][] {// - // - { AlwaysFailingBeforeTestClassTestCase.class.getSimpleName() },// - { AlwaysFailingAfterTestClassTestCase.class.getSimpleName() },// - { AlwaysFailingPrepareTestInstanceTestCase.class.getSimpleName() },// - { AlwaysFailingBeforeTestMethodTestCase.class.getSimpleName() },// - { AlwaysFailingAfterTestMethodTestCase.class.getSimpleName() },// - { FailingBeforeTransactionTestCase.class.getSimpleName() },// - { FailingAfterTransactionTestCase.class.getSimpleName() } // - }); + public static Object[] testData() { + return new Object[] {// + AlwaysFailingBeforeTestClassTestCase.class.getSimpleName(),// + AlwaysFailingAfterTestClassTestCase.class.getSimpleName(),// + AlwaysFailingPrepareTestInstanceTestCase.class.getSimpleName(),// + AlwaysFailingBeforeTestMethodTestCase.class.getSimpleName(),// + AlwaysFailingAfterTestMethodTestCase.class.getSimpleName(),// + FailingBeforeTransactionTestCase.class.getSimpleName(),// + FailingAfterTransactionTestCase.class.getSimpleName() // + }; } public FailingBeforeAndAfterMethodsJUnitTests(String testClassName) throws Exception { this.clazz = ClassUtils.forName(getClass().getName() + "." + testClassName, getClass().getClassLoader()); } + protected Runner getRunner(Class testClass) throws Exception { + Class runnerClass = testClass.getAnnotation(RunWith.class).value(); + Constructor constructor = runnerClass.getConstructor(Class.class); + return (Runner) BeanUtils.instantiateClass(constructor, testClass); + } + @Test public void runTestAndAssertCounters() throws Exception { - final TrackingRunListener listener = new TrackingRunListener(); - final RunNotifier notifier = new RunNotifier(); + TrackingRunListener listener = new TrackingRunListener(); + RunNotifier notifier = new RunNotifier(); notifier.addListener(listener); - new SpringJUnit4ClassRunner(this.clazz).run(notifier); - assertEquals("Verifying number of failures for test class [" + this.clazz + "].", 1, - listener.getTestFailureCount()); + getRunner(this.clazz).run(notifier); + assertEquals("Failures for test class [" + this.clazz + "].", 1, listener.getTestFailureCount()); } // ------------------------------------------------------------------- - static class AlwaysFailingBeforeTestClassTestExecutionListener extends AbstractTestExecutionListener { + protected static class AlwaysFailingBeforeTestClassTestExecutionListener extends AbstractTestExecutionListener { @Override public void beforeTestClass(TestContext testContext) { @@ -107,7 +108,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests { } } - static class AlwaysFailingAfterTestClassTestExecutionListener extends AbstractTestExecutionListener { + protected static class AlwaysFailingAfterTestClassTestExecutionListener extends AbstractTestExecutionListener { @Override public void afterTestClass(TestContext testContext) { @@ -115,7 +116,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests { } } - static class AlwaysFailingPrepareTestInstanceTestExecutionListener extends AbstractTestExecutionListener { + protected static class AlwaysFailingPrepareTestInstanceTestExecutionListener extends AbstractTestExecutionListener { @Override public void prepareTestInstance(TestContext testContext) throws Exception { @@ -123,7 +124,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests { } } - static class AlwaysFailingBeforeTestMethodTestExecutionListener extends AbstractTestExecutionListener { + protected static class AlwaysFailingBeforeTestMethodTestExecutionListener extends AbstractTestExecutionListener { @Override public void beforeTestMethod(TestContext testContext) { @@ -131,7 +132,7 @@ public class FailingBeforeAndAfterMethodsJUnitTests { } } - static class AlwaysFailingAfterTestMethodTestExecutionListener extends AbstractTestExecutionListener { + protected static class AlwaysFailingAfterTestMethodTestExecutionListener extends AbstractTestExecutionListener { @Override public void afterTestMethod(TestContext testContext) { @@ -174,8 +175,10 @@ public class FailingBeforeAndAfterMethodsJUnitTests { } @Ignore("TestCase classes are run manually by the enclosing test class") + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("FailingBeforeAndAfterMethodsTests-context.xml") - public static class FailingBeforeTransactionTestCase extends AbstractTransactionalJUnit4SpringContextTests { + @Transactional + public static class FailingBeforeTransactionTestCase { @Test public void testNothing() { @@ -188,8 +191,10 @@ public class FailingBeforeAndAfterMethodsJUnitTests { } @Ignore("TestCase classes are run manually by the enclosing test class") + @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("FailingBeforeAndAfterMethodsTests-context.xml") - public static class FailingAfterTransactionTestCase extends AbstractTransactionalJUnit4SpringContextTests { + @Transactional + public static class FailingAfterTransactionTestCase { @Test public void testNothing() { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.java index 95b021c572..aa17c58338 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -16,8 +16,6 @@ package org.springframework.test.context.junit4; -import org.junit.runner.RunWith; - import org.springframework.test.context.ContextConfiguration; import org.springframework.util.ResourceUtils; @@ -36,7 +34,6 @@ import org.springframework.util.ResourceUtils; * @since 2.5 * @see SpringJUnit4ClassRunnerAppCtxTests */ -@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration( { MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.CLASSPATH_RESOURCE_PATH, MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.LOCAL_RESOURCE_PATH, MultipleResourcesSpringJUnit4ClassRunnerAppCtxTests.ABSOLUTE_RESOURCE_PATH }) diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests.java index 7d57eb1128..0d20a4bcce 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests.java @@ -16,10 +16,7 @@ package org.springframework.test.context.junit4; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.AfterClass; import org.junit.Before; @@ -51,15 +48,16 @@ import static org.junit.Assert.*; * * @author Sam Brannen * @since 2.5 + * @see org.springframework.test.context.junit4.rules.ParameterizedSpringRuleTests */ @RunWith(Parameterized.class) @ContextConfiguration @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class }) public class ParameterizedDependencyInjectionTests { - private static final List employees = new ArrayList(); + private static final AtomicInteger invocationCount = new AtomicInteger(); - private final TestContextManager testContextManager = new TestContextManager(getClass()); + private static final TestContextManager testContextManager = new TestContextManager(ParameterizedDependencyInjectionTests.class); @Autowired private ApplicationContext applicationContext; @@ -75,37 +73,37 @@ public class ParameterizedDependencyInjectionTests { @Parameters(name = "bean [{0}], employee [{1}]") - public static Collection employeeData() { - return Arrays.asList(new String[][] { { "employee1", "John Smith" }, { "employee2", "Jane Smith" } }); + public static String[][] employeeData() { + return new String[][] { { "employee1", "John Smith" }, { "employee2", "Jane Smith" } }; } @BeforeClass - public static void clearEmployees() { - employees.clear(); + public static void BeforeClass() { + invocationCount.set(0); } @Before - public void injectDependencies() throws Throwable { - this.testContextManager.prepareTestInstance(this); + public void injectDependencies() throws Exception { + testContextManager.prepareTestInstance(this); } @Test public final void verifyPetAndEmployee() { + invocationCount.incrementAndGet(); // Verifying dependency injection: assertNotNull("The pet field should have been autowired.", this.pet); // Verifying 'parameterized' support: - final Employee employee = (Employee) this.applicationContext.getBean(this.employeeBeanName); - employees.add(employee); - assertEquals("Verifying the name of the employee configured as bean [" + this.employeeBeanName + "].", - this.employeeName, employee.getName()); + Employee employee = this.applicationContext.getBean(this.employeeBeanName, Employee.class); + assertEquals("Name of the employee configured as bean [" + this.employeeBeanName + "].", this.employeeName, + employee.getName()); } @AfterClass public static void verifyNumParameterizedRuns() { - assertEquals("Verifying the number of times the parameterized test method was executed.", - employeeData().size(), employees.size()); + assertEquals("Number of times the parameterized test method was executed.", employeeData().length, + invocationCount.get()); } } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/RelativePathSpringJUnit4ClassRunnerAppCtxTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/RelativePathSpringJUnit4ClassRunnerAppCtxTests.java index 478874a747..e681b3ad55 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/RelativePathSpringJUnit4ClassRunnerAppCtxTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/RelativePathSpringJUnit4ClassRunnerAppCtxTests.java @@ -16,8 +16,6 @@ package org.springframework.test.context.junit4; -import org.junit.runner.RunWith; - import org.springframework.test.context.ContextConfiguration; /** @@ -30,7 +28,6 @@ import org.springframework.test.context.ContextConfiguration; * @see SpringJUnit4ClassRunnerAppCtxTests * @see AbsolutePathSpringJUnit4ClassRunnerAppCtxTests */ -@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "SpringJUnit4ClassRunnerAppCtxTests-context.xml" }) public class RelativePathSpringJUnit4ClassRunnerAppCtxTests extends SpringJUnit4ClassRunnerAppCtxTests { /* all tests are in the parent class. */ diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java index a910ce0721..9195d4cbdd 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/RepeatedSpringRunnerTests.java @@ -19,13 +19,12 @@ package org.springframework.test.context.junit4; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; -import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -41,8 +40,8 @@ import static org.junit.Assert.*; * Verifies proper handling of the following in conjunction with the * {@link SpringJUnit4ClassRunner}: *
      - *
    • Spring's {@link Repeat @Repeat}
    • - *
    • Spring's {@link Timed @Timed}
    • + *
    • Spring's {@link Repeat @Repeat}
    • + *
    • Spring's {@link Timed @Timed}
    • *
    * * @author Sam Brannen @@ -51,22 +50,19 @@ import static org.junit.Assert.*; @RunWith(Parameterized.class) public class RepeatedSpringRunnerTests { - private static final AtomicInteger invocationCount = new AtomicInteger(); + protected static final AtomicInteger invocationCount = new AtomicInteger(); private final Class testClass; private final int expectedFailureCount; - private final int expectedTestStartedCount; - private final int expectedTestFinishedCount; - private final int expectedInvocationCount; @Parameters(name = "{0}") - public static Collection repetitionData() { - return Arrays.asList(new Object[][] {// + public static Object[][] repetitionData() { + return new Object[][] {// // { NonAnnotatedRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 1 },// { DefaultRepeatValueRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 1 },// @@ -74,7 +70,7 @@ public class RepeatedSpringRunnerTests { { RepeatedFiveTimesRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 5 },// { RepeatedFiveTimesViaMetaAnnotationRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 5 },// { TimedRepeatedTestCase.class.getSimpleName(), 3, 4, 4, (5 + 1 + 4 + 10) } // - }); + }; } public RepeatedSpringRunnerTests(String testClassName, int expectedFailureCount, @@ -86,6 +82,10 @@ public class RepeatedSpringRunnerTests { this.expectedInvocationCount = expectedInvocationCount; } + protected Runner getRunner(Class testClass) throws Exception { + return new SpringJUnit4ClassRunner(testClass); + } + @Test public void assertRepetitions() throws Exception { TrackingRunListener listener = new TrackingRunListener(); @@ -93,19 +93,14 @@ public class RepeatedSpringRunnerTests { notifier.addListener(listener); invocationCount.set(0); - new SpringJUnit4ClassRunner(this.testClass).run(notifier); - assertEquals("Verifying number of failures for test class [" + this.testClass + "].", - this.expectedFailureCount, listener.getTestFailureCount()); - assertEquals("Verifying number of tests started for test class [" + this.testClass + "].", - this.expectedTestStartedCount, listener.getTestStartedCount()); - assertEquals("Verifying number of tests finished for test class [" + this.testClass + "].", - this.expectedTestFinishedCount, listener.getTestFinishedCount()); - assertEquals("Verifying number of invocations for test class [" + this.testClass + "].", - this.expectedInvocationCount, invocationCount.get()); + getRunner(this.testClass).run(notifier); + assertEquals("failures for [" + testClass + "].", expectedFailureCount, listener.getTestFailureCount()); + assertEquals("tests started for [" + testClass + "].", expectedTestStartedCount, listener.getTestStartedCount()); + assertEquals("tests finished for [" + testClass + "].", expectedTestFinishedCount, listener.getTestFinishedCount()); + assertEquals("invocations for [" + testClass + "].", expectedInvocationCount, invocationCount.get()); } - @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({}) public abstract static class AbstractRepeatedTestCase { @@ -167,9 +162,7 @@ public class RepeatedSpringRunnerTests { } /** - * Unit tests for claims raised in SPR-6011. + * Unit tests for claims raised in SPR-6011. */ @Ignore("TestCase classes are run manually by the enclosing test class") public static final class TimedRepeatedTestCase extends AbstractRepeatedTestCase { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java index b463545169..6aff94bbad 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4TestSuite.java @@ -21,6 +21,7 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import org.springframework.test.context.cache.ClassLevelDirtiesContextTests; import org.springframework.test.context.cache.SpringRunnerContextCacheTests; +import org.springframework.test.context.jdbc.RequiresNewTransactionSqlScriptsTests; import org.springframework.test.context.junit4.annotation.AnnotationConfigSpringJUnit4ClassRunnerAppCtxTests; import org.springframework.test.context.junit4.annotation.BeanOverridingDefaultConfigClassesInheritedTests; import org.springframework.test.context.junit4.annotation.BeanOverridingExplicitConfigClassesInheritedTests; @@ -41,6 +42,7 @@ import org.springframework.test.context.junit4.profile.annotation.DevProfileReso import org.springframework.test.context.junit4.profile.xml.DefaultProfileXmlConfigTests; import org.springframework.test.context.junit4.profile.xml.DevProfileResolverXmlConfigTests; import org.springframework.test.context.junit4.profile.xml.DevProfileXmlConfigTests; +import org.springframework.test.context.transaction.programmatic.ProgrammaticTxMgmtTests; /** * JUnit test suite for tests involving {@link SpringJUnit4ClassRunner} and the @@ -99,6 +101,7 @@ StandardJUnit4FeaturesTests.class,// SpringRunnerContextCacheTests.class,// ClassLevelDirtiesContextTests.class,// ParameterizedDependencyInjectionTests.class,// + ConcreteTransactionalJUnit4SpringContextTests.class,// ClassLevelTransactionalSpringRunnerTests.class,// MethodLevelTransactionalSpringRunnerTests.class,// DefaultRollbackTrueTransactionalSpringRunnerTests.class,// @@ -107,6 +110,8 @@ StandardJUnit4FeaturesTests.class,// RollbackOverrideDefaultRollbackFalseTransactionalSpringRunnerTests.class,// BeforeAndAfterTransactionAnnotationTests.class,// TimedTransactionalSpringRunnerTests.class,// + ProgrammaticTxMgmtTests.class,// + RequiresNewTransactionSqlScriptsTests.class,// HibernateSessionFlushingTests.class // }) public class SpringJUnit4TestSuite { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java index df65489837..7061307e22 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/TimedSpringRunnerTests.java @@ -22,13 +22,12 @@ import java.lang.annotation.RetentionPolicy; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runner.Runner; import org.junit.runner.notification.RunNotifier; import org.junit.runners.JUnit4; import org.springframework.test.annotation.Timed; import org.springframework.test.context.TestExecutionListeners; -import org.springframework.tests.Assume; -import org.springframework.tests.TestGroup; import static org.junit.Assert.*; @@ -46,32 +45,33 @@ import static org.junit.Assert.*; @RunWith(JUnit4.class) public class TimedSpringRunnerTests { + protected Class getTestCase() { + return TimedSpringRunnerTestCase.class; + } + + protected Runner getRunner(Class testClass) throws Exception { + return new SpringJUnit4ClassRunner(testClass); + } + @Test public void timedTests() throws Exception { - Assume.group(TestGroup.PERFORMANCE); - Class testClass = TimedSpringRunnerTestCase.class; + Class testClass = getTestCase(); TrackingRunListener listener = new TrackingRunListener(); RunNotifier notifier = new RunNotifier(); notifier.addListener(listener); - new SpringJUnit4ClassRunner(testClass).run(notifier); - assertEquals("Verifying number of tests started for test class [" + testClass + "].", 7, - listener.getTestStartedCount()); - assertEquals("Verifying number of tests ignored for test class [" + testClass + "].", 0, - listener.getTestIgnoredCount()); - assertEquals("Verifying number of assumption failures for test class [" + testClass + "].", 0, - listener.getTestAssumptionFailureCount()); - assertEquals("Verifying number of test failures for test class [" + testClass + "].", 5, - listener.getTestFailureCount()); - assertEquals("Verifying number of tests finished for test class [" + testClass + "].", 7, - listener.getTestFinishedCount()); + getRunner(testClass).run(notifier); + assertEquals("tests started for [" + testClass + "].", 7, listener.getTestStartedCount()); + assertEquals("tests ignored for [" + testClass + "].", 0, listener.getTestIgnoredCount()); + assertEquals("assumption failures for [" + testClass + "].", 0, listener.getTestAssumptionFailureCount()); + assertEquals("test failures for [" + testClass + "].", 5, listener.getTestFailureCount()); + assertEquals("tests finished for [" + testClass + "].", 7, listener.getTestFinishedCount()); } @Ignore("TestCase classes are run manually by the enclosing test class") - @RunWith(SpringJUnit4ClassRunner.class) @TestExecutionListeners({}) - public static final class TimedSpringRunnerTestCase { + public static class TimedSpringRunnerTestCase { // Should Pass. @Test(timeout = 2000) diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BaseAppCtxRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BaseAppCtxRuleTests.java new file mode 100644 index 0000000000..628fc5f0be --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BaseAppCtxRuleTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import static org.junit.Assert.*; + +/** + * Base class for integration tests involving Spring {@code ApplicationContexts} + * in conjunction with {@link SpringClassRule} and {@link SpringMethodRule}. + * + *

    The goal of this class and its subclasses is to ensure that Rule-based + * configuration can be inherited without requiring {@link SpringClassRule} + * or {@link SpringMethodRule} to be redeclared on subclasses. + * + * @author Sam Brannen + * @since 4.2 + * @see Subclass1AppCtxRuleTests + * @see Subclass2AppCtxRuleTests + */ +@ContextConfiguration +public class BaseAppCtxRuleTests { + + @Configuration + static class Config { + + @Bean + public String foo() { + return "foo"; + } + } + + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + @Autowired + String foo; + + + @Test + public void test() { + assertEquals("foo", foo); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BasicAnnotationConfigWacSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BasicAnnotationConfigWacSpringRuleTests.java new file mode 100644 index 0000000000..8b714cd0e2 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BasicAnnotationConfigWacSpringRuleTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import org.springframework.test.context.web.BasicAnnotationConfigWacTests; + +/** + * This class is an extension of {@link BasicAnnotationConfigWacTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +@RunWith(JUnit4.class) +public class BasicAnnotationConfigWacSpringRuleTests extends BasicAnnotationConfigWacTests { + + // All tests are in superclass. + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BeforeAndAfterTransactionAnnotationSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BeforeAndAfterTransactionAnnotationSpringRuleTests.java new file mode 100644 index 0000000000..e936a0fba1 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/BeforeAndAfterTransactionAnnotationSpringRuleTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import org.springframework.test.context.junit4.BeforeAndAfterTransactionAnnotationTests; + +/** + * This class is an extension of {@link BeforeAndAfterTransactionAnnotationTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +@RunWith(JUnit4.class) +public class BeforeAndAfterTransactionAnnotationSpringRuleTests extends BeforeAndAfterTransactionAnnotationTests { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + // All tests are in superclass. + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ClassLevelDisabledSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ClassLevelDisabledSpringRuleTests.java new file mode 100644 index 0000000000..e8fb3d4ea5 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ClassLevelDisabledSpringRuleTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import org.springframework.test.context.junit4.ClassLevelDisabledSpringRunnerTests; + +/** + * This class is an extension of {@link ClassLevelDisabledSpringRunnerTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +@RunWith(JUnit4.class) +public class ClassLevelDisabledSpringRuleTests extends ClassLevelDisabledSpringRunnerTests { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + // All tests are in superclass. + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/EnabledAndIgnoredSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/EnabledAndIgnoredSpringRuleTests.java new file mode 100644 index 0000000000..234569e7fc --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/EnabledAndIgnoredSpringRuleTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import org.springframework.test.context.junit4.EnabledAndIgnoredSpringRunnerTests; + +/** + * This class is an extension of {@link EnabledAndIgnoredSpringRunnerTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +@RunWith(JUnit4.class) +public class EnabledAndIgnoredSpringRuleTests extends EnabledAndIgnoredSpringRunnerTests { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + // All tests are in superclass. + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/FailingBeforeAndAfterMethodsSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/FailingBeforeAndAfterMethodsSpringRuleTests.java new file mode 100644 index 0000000000..e9d1461c07 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/FailingBeforeAndAfterMethodsSpringRuleTests.java @@ -0,0 +1,152 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.Parameterized.Parameters; + +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.FailingBeforeAndAfterMethodsJUnitTests; +import org.springframework.test.context.transaction.AfterTransaction; +import org.springframework.test.context.transaction.BeforeTransaction; +import org.springframework.transaction.annotation.Transactional; + +import static org.junit.Assert.*; + +/** + * This class is an extension of {@link FailingBeforeAndAfterMethodsJUnitTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +public class FailingBeforeAndAfterMethodsSpringRuleTests extends FailingBeforeAndAfterMethodsJUnitTests { + + @Parameters(name = "{0}") + public static Object[] testData() { + return new Object[] {// + AlwaysFailingBeforeTestClassSpringRuleTestCase.class.getSimpleName(),// + AlwaysFailingAfterTestClassSpringRuleTestCase.class.getSimpleName(),// + AlwaysFailingPrepareTestInstanceSpringRuleTestCase.class.getSimpleName(),// + AlwaysFailingBeforeTestMethodSpringRuleTestCase.class.getSimpleName(),// + AlwaysFailingAfterTestMethodSpringRuleTestCase.class.getSimpleName(),// + FailingBeforeTransactionSpringRuleTestCase.class.getSimpleName(),// + FailingAfterTransactionSpringRuleTestCase.class.getSimpleName() // + }; + } + + public FailingBeforeAndAfterMethodsSpringRuleTests(String testClassName) throws Exception { + super(testClassName); + } + + // All tests are in superclass. + + @RunWith(JUnit4.class) + @TestExecutionListeners({}) + public static abstract class BaseSpringRuleTestCase { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + + @Test + public void testNothing() { + } + } + + @Ignore("TestCase classes are run manually by the enclosing test class") + @TestExecutionListeners(AlwaysFailingBeforeTestClassTestExecutionListener.class) + public static class AlwaysFailingBeforeTestClassSpringRuleTestCase extends BaseSpringRuleTestCase { + } + + @Ignore("TestCase classes are run manually by the enclosing test class") + @TestExecutionListeners(AlwaysFailingAfterTestClassTestExecutionListener.class) + public static class AlwaysFailingAfterTestClassSpringRuleTestCase extends BaseSpringRuleTestCase { + } + + @Ignore("TestCase classes are run manually by the enclosing test class") + @TestExecutionListeners(AlwaysFailingPrepareTestInstanceTestExecutionListener.class) + public static class AlwaysFailingPrepareTestInstanceSpringRuleTestCase extends BaseSpringRuleTestCase { + } + + @Ignore("TestCase classes are run manually by the enclosing test class") + @TestExecutionListeners(AlwaysFailingBeforeTestMethodTestExecutionListener.class) + public static class AlwaysFailingBeforeTestMethodSpringRuleTestCase extends BaseSpringRuleTestCase { + } + + @Ignore("TestCase classes are run manually by the enclosing test class") + @TestExecutionListeners(AlwaysFailingAfterTestMethodTestExecutionListener.class) + public static class AlwaysFailingAfterTestMethodSpringRuleTestCase extends BaseSpringRuleTestCase { + } + + @Ignore("TestCase classes are run manually by the enclosing test class") + @RunWith(JUnit4.class) + @ContextConfiguration("../FailingBeforeAndAfterMethodsTests-context.xml") + @Transactional + public static class FailingBeforeTransactionSpringRuleTestCase { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + + @Test + public void testNothing() { + } + + @BeforeTransaction + public void beforeTransaction() { + fail("always failing beforeTransaction()"); + } + } + + @Ignore("TestCase classes are run manually by the enclosing test class") + @RunWith(JUnit4.class) + @ContextConfiguration("../FailingBeforeAndAfterMethodsTests-context.xml") + @Transactional + public static class FailingAfterTransactionSpringRuleTestCase { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + + @Test + public void testNothing() { + } + + @AfterTransaction + public void afterTransaction() { + fail("always failing afterTransaction()"); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ParameterizedSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ParameterizedSpringRuleTests.java new file mode 100644 index 0000000000..862ea065a1 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ParameterizedSpringRuleTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.tests.sample.beans.Employee; +import org.springframework.tests.sample.beans.Pet; + +import static org.junit.Assert.*; + +/** + * Integration test which demonstrates how to use JUnit's {@link Parameterized} + * runner in conjunction with {@link SpringClassRule} and {@link SpringMethodRule} + * to provide dependency injection to a parameterized test instance. + * + * @author Sam Brannen + * @since 4.2 + * @see org.springframework.test.context.junit4.ParameterizedDependencyInjectionTests + */ +@RunWith(Parameterized.class) +@ContextConfiguration("/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests-context.xml") +public class ParameterizedSpringRuleTests { + + private static final AtomicInteger invocationCount = new AtomicInteger(); + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private Pet pet; + + @Parameter(0) + public String employeeBeanName; + + @Parameter(1) + public String employeeName; + + + @Parameters(name = "bean [{0}], employee [{1}]") + public static String[][] employeeData() { + return new String[][] { { "employee1", "John Smith" }, { "employee2", "Jane Smith" } }; + } + + @BeforeClass + public static void BeforeClass() { + invocationCount.set(0); + } + + @Test + public final void verifyPetAndEmployee() { + invocationCount.incrementAndGet(); + + // Verifying dependency injection: + assertNotNull("The pet field should have been autowired.", this.pet); + + // Verifying 'parameterized' support: + Employee employee = this.applicationContext.getBean(this.employeeBeanName, Employee.class); + assertEquals("Name of the employee configured as bean [" + this.employeeBeanName + "].", this.employeeName, + employee.getName()); + } + + @AfterClass + public static void verifyNumParameterizedRuns() { + assertEquals("Number of times the parameterized test method was executed.", employeeData().length, + invocationCount.get()); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ProgrammaticTxMgmtSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ProgrammaticTxMgmtSpringRuleTests.java new file mode 100644 index 0000000000..524b3e22c5 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/ProgrammaticTxMgmtSpringRuleTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import javax.sql.DataSource; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.transaction.programmatic.ProgrammaticTxMgmtTests; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * This class is an extension of {@link ProgrammaticTxMgmtTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +@RunWith(JUnit4.class) +@ContextConfiguration +public class ProgrammaticTxMgmtSpringRuleTests extends ProgrammaticTxMgmtTests { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + // All tests are in superclass. + + // ------------------------------------------------------------------------- + + @Configuration + static class Config { + + @Bean + public PlatformTransactionManager transactionManager() { + return new DataSourceTransactionManager(dataSource()); + } + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder()// + .generateUniqueName(true)// + .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql") // + .build(); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/RepeatedSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/RepeatedSpringRuleTests.java new file mode 100644 index 0000000000..f66eec5dcd --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/RepeatedSpringRuleTests.java @@ -0,0 +1,179 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.Runner; +import org.junit.runners.JUnit4; +import org.junit.runners.Parameterized.Parameters; + +import org.springframework.test.annotation.Repeat; +import org.springframework.test.annotation.Timed; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.RepeatedSpringRunnerTests; + +/** + * This class is an extension of {@link RepeatedSpringRunnerTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +public class RepeatedSpringRuleTests extends RepeatedSpringRunnerTests { + + @Parameters(name = "{0}") + public static Object[][] repetitionData() { + return new Object[][] {// + // + { NonAnnotatedRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 1 },// + { DefaultRepeatValueRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 1 },// + { NegativeRepeatValueRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 1 },// + { RepeatedFiveTimesRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 5 },// + { RepeatedFiveTimesViaMetaAnnotationRepeatedTestCase.class.getSimpleName(), 0, 1, 1, 5 },// + { TimedRepeatedTestCase.class.getSimpleName(), 3, 4, 4, (5 + 1 + 4 + 10) } // + }; + } + + public RepeatedSpringRuleTests(String testClassName, int expectedFailureCount, int expectedTestStartedCount, + int expectedTestFinishedCount, int expectedInvocationCount) throws Exception { + + super(testClassName, expectedFailureCount, expectedTestStartedCount, expectedTestFinishedCount, + expectedInvocationCount); + } + + @Override + protected Runner getRunner(Class testClass) throws Exception { + return new JUnit4(testClass); + } + + + // All tests are in superclass. + + @TestExecutionListeners({}) + public abstract static class AbstractRepeatedTestCase { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + + protected void incrementInvocationCount() throws IOException { + invocationCount.incrementAndGet(); + } + } + + public static final class NonAnnotatedRepeatedTestCase extends AbstractRepeatedTestCase { + + @Test + @Timed(millis = 10000) + public void nonAnnotated() throws Exception { + incrementInvocationCount(); + } + } + + public static final class DefaultRepeatValueRepeatedTestCase extends AbstractRepeatedTestCase { + + @Test + @Repeat + @Timed(millis = 10000) + public void defaultRepeatValue() throws Exception { + incrementInvocationCount(); + } + } + + public static final class NegativeRepeatValueRepeatedTestCase extends AbstractRepeatedTestCase { + + @Test + @Repeat(-5) + @Timed(millis = 10000) + public void negativeRepeatValue() throws Exception { + incrementInvocationCount(); + } + } + + public static final class RepeatedFiveTimesRepeatedTestCase extends AbstractRepeatedTestCase { + + @Test + @Repeat(5) + public void repeatedFiveTimes() throws Exception { + incrementInvocationCount(); + } + } + + @Repeat(5) + @Retention(RetentionPolicy.RUNTIME) + private static @interface RepeatedFiveTimes { + } + + public static final class RepeatedFiveTimesViaMetaAnnotationRepeatedTestCase extends AbstractRepeatedTestCase { + + @Test + @RepeatedFiveTimes + public void repeatedFiveTimes() throws Exception { + incrementInvocationCount(); + } + } + + /** + * Unit tests for claims raised in SPR-6011. + */ + @Ignore("TestCase classes are run manually by the enclosing test class") + public static final class TimedRepeatedTestCase extends AbstractRepeatedTestCase { + + @Test + @Timed(millis = 1000) + @Repeat(5) + public void repeatedFiveTimesButDoesNotExceedTimeout() throws Exception { + incrementInvocationCount(); + } + + @Test + @Timed(millis = 10) + @Repeat(1) + public void singleRepetitionExceedsTimeout() throws Exception { + incrementInvocationCount(); + Thread.sleep(15); + } + + @Test + @Timed(millis = 20) + @Repeat(4) + public void firstRepetitionOfManyExceedsTimeout() throws Exception { + incrementInvocationCount(); + Thread.sleep(25); + } + + @Test + @Timed(millis = 100) + @Repeat(10) + public void collectiveRepetitionsExceedTimeout() throws Exception { + incrementInvocationCount(); + Thread.sleep(11); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/Subclass1AppCtxRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/Subclass1AppCtxRuleTests.java new file mode 100644 index 0000000000..1f2e4955f9 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/Subclass1AppCtxRuleTests.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +/** + * Subclass #1 of {@link BaseAppCtxRuleTests}. + * + * @author Sam Brannen + * @since 4.2 + */ +public class Subclass1AppCtxRuleTests extends BaseAppCtxRuleTests { + + // All tests and config are in superclass. + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/Subclass2AppCtxRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/Subclass2AppCtxRuleTests.java new file mode 100644 index 0000000000..9d277be11a --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/Subclass2AppCtxRuleTests.java @@ -0,0 +1,29 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +/** + * Subclass #2 of {@link BaseAppCtxRuleTests}. + * + * @author Sam Brannen + * @since 4.2 + */ +public class Subclass2AppCtxRuleTests extends BaseAppCtxRuleTests { + + // All tests and config are in superclass. + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TimedSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TimedSpringRuleTests.java new file mode 100644 index 0000000000..17a3e8949c --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TimedSpringRuleTests.java @@ -0,0 +1,77 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.runner.Runner; +import org.junit.runners.JUnit4; + +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.TimedSpringRunnerTests; + +import static org.junit.Assert.*; + +/** + * This class is an extension of {@link TimedSpringRunnerTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +public class TimedSpringRuleTests extends TimedSpringRunnerTests { + + // All tests are in superclass. + + @Override + protected Class getTestCase() { + return TimedSpringRuleTestCase.class; + } + + @Override + protected Runner getRunner(Class testClass) throws Exception { + return new JUnit4(testClass); + } + + + @Ignore("TestCase classes are run manually by the enclosing test class") + @TestExecutionListeners({}) + public static final class TimedSpringRuleTestCase extends TimedSpringRunnerTestCase { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + + /** + * Overridden to always throw an exception, since Spring's Rule-based + * JUnit integration does not fail a test for duplicate configuration + * of timeouts. + */ + @Override + public void springAndJUnitTimeouts() { + fail("intentional failure to make tests in superclass pass"); + } + + // All other tests are in superclass. + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TimedTransactionalSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TimedTransactionalSpringRuleTests.java new file mode 100644 index 0000000000..ca91af6a6b --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TimedTransactionalSpringRuleTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import java.util.concurrent.TimeUnit; + +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import org.springframework.test.annotation.Repeat; +import org.springframework.test.context.junit4.TimedTransactionalSpringRunnerTests; + +import static org.springframework.test.transaction.TransactionTestUtils.*; + +/** + * This class is an extension of {@link TimedTransactionalSpringRunnerTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +@RunWith(JUnit4.class) +public class TimedTransactionalSpringRuleTests extends TimedTransactionalSpringRunnerTests { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + @Rule + public Timeout timeout = Timeout.builder().withTimeout(10, TimeUnit.SECONDS).build(); + + + /** + * Overridden since Spring's Rule-based JUnit support cannot properly + * integrate with timed execution that is controlled by a third-party runner. + */ + @Test(timeout = 10000) + @Repeat(5) + @Override + public void transactionalWithJUnitTimeout() { + assertInTransaction(false); + } + + /** + * {@code timeout} explicitly not declared due to presence of Timeout rule. + */ + @Test + public void transactionalWithJUnitRuleBasedTimeout() { + assertInTransaction(true); + } + + // All other tests are in superclass. + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TransactionalSqlScriptsSpringRuleTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TransactionalSqlScriptsSpringRuleTests.java new file mode 100644 index 0000000000..d6506f0259 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/rules/TransactionalSqlScriptsSpringRuleTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2015 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.junit4.rules; + +import java.util.concurrent.TimeUnit; + +import org.junit.ClassRule; +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.Timeout; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.MethodSorters; + +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.TransactionalSqlScriptsTests; + +/** + * This class is an extension of {@link TransactionalSqlScriptsTests} + * that has been modified to use {@link SpringClassRule} and + * {@link SpringMethodRule}. + * + * @author Sam Brannen + * @since 4.2 + */ +@RunWith(JUnit4.class) +// Note: @FixMethodOrder is NOT @Inherited. +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +// Overriding @Sql declaration to reference scripts using relative path. +@Sql({ "../../jdbc/schema.sql", "../../jdbc/data.sql" }) +public class TransactionalSqlScriptsSpringRuleTests extends TransactionalSqlScriptsTests { + + @ClassRule + public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); + + @Rule + public final SpringMethodRule springMethodRule = new SpringMethodRule(this); + + @Rule + public Timeout timeout = Timeout.builder().withTimeout(10, TimeUnit.SECONDS).build(); + + + /** + * Redeclared to ensure that {@code @FixMethodOrder} is properly applied. + */ + @Test + @Override + // test##_ prefix is required for @FixMethodOrder. + public void test01_classLevelScripts() { + assertNumUsers(1); + } + + /** + * Overriding {@code @Sql} declaration to reference scripts using relative path. + */ + @Test + @Sql({ "../../jdbc/drop-schema.sql", "../../jdbc/schema.sql", "../../jdbc/data.sql", "../../jdbc/data-add-dogbert.sql" }) + @Override + // test##_ prefix is required for @FixMethodOrder. + public void test02_methodLevelScripts() { + assertNumUsers(2); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/transaction/programmatic/ProgrammaticTxMgmtTests.java b/spring-test/src/test/java/org/springframework/test/context/transaction/programmatic/ProgrammaticTxMgmtTests.java index b193ea5f1d..35f71b2229 100644 --- a/spring-test/src/test/java/org/springframework/test/context/transaction/programmatic/ProgrammaticTxMgmtTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/transaction/programmatic/ProgrammaticTxMgmtTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -25,16 +25,25 @@ import javax.sql.DataSource; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.Resource; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.transaction.AfterTransaction; import org.springframework.test.context.transaction.BeforeTransaction; import org.springframework.test.context.transaction.TestTransaction; +import org.springframework.test.jdbc.JdbcTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -49,13 +58,28 @@ import static org.springframework.test.transaction.TransactionTestUtils.*; * @author Sam Brannen * @since 4.1 */ +@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -public class ProgrammaticTxMgmtTests extends AbstractTransactionalJUnit4SpringContextTests { +@Transactional +public class ProgrammaticTxMgmtTests { + + private String sqlScriptEncoding; + + protected JdbcTemplate jdbcTemplate; + + @Autowired + protected ApplicationContext applicationContext; @Rule public TestName testName = new TestName(); + @Autowired + public void setDataSource(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + @BeforeTransaction public void beforeTransaction() { deleteFromTables("user"); @@ -242,6 +266,15 @@ public class ProgrammaticTxMgmtTests extends AbstractTransactionalJUnit4SpringCo // ------------------------------------------------------------------------- + protected int deleteFromTables(String... names) { + return JdbcTestUtils.deleteFromTables(this.jdbcTemplate, names); + } + + protected void executeSqlScript(String sqlResourcePath, boolean continueOnError) throws DataAccessException { + Resource resource = this.applicationContext.getResource(sqlResourcePath); + new ResourceDatabasePopulator(continueOnError, false, this.sqlScriptEncoding, resource).execute(jdbcTemplate.getDataSource()); + } + private void assertUsers(String... users) { List expected = Arrays.asList(users); Collections.sort(expected); @@ -250,7 +283,6 @@ public class ProgrammaticTxMgmtTests extends AbstractTransactionalJUnit4SpringCo assertEquals("Users in database;", expected, actual); } - // ------------------------------------------------------------------------- @Configuration @@ -264,7 +296,7 @@ public class ProgrammaticTxMgmtTests extends AbstractTransactionalJUnit4SpringCo @Bean public DataSource dataSource() { return new EmbeddedDatabaseBuilder()// - .setName("programmatic-tx-mgmt-test-db")// + .generateUniqueName(true)// .addScript("classpath:/org/springframework/test/context/jdbc/schema.sql") // .build(); } diff --git a/spring-test/src/test/resources/log4j.properties b/spring-test/src/test/resources/log4j.properties index fee90d1398..2af173af30 100644 --- a/spring-test/src/test/resources/log4j.properties +++ b/spring-test/src/test/resources/log4j.properties @@ -10,13 +10,17 @@ log4j.appender.file.layout.ConversionPattern=%d{HH:mm:ss,SSS} [%c] - %m%n log4j.rootCategory=ERROR, console, file log4j.logger.org.springframework.beans=WARN + log4j.logger.org.springframework.test.context.TestContext=WARN +log4j.logger.org.springframework.test.context.TestContextManager=WARN log4j.logger.org.springframework.test.context.ContextLoaderUtils=WARN log4j.logger.org.springframework.test.context.transaction.TransactionalTestExecutionListener=WARN log4j.logger.org.springframework.test.context.web=WARN log4j.logger.org.springframework.test.context=WARN log4j.logger.org.springframework.test.context.cache=WARN +log4j.logger.org.springframework.test.context.junit4.rules=WARN + #log4j.logger.org.springframework.test.context.support=INFO #log4j.logger.org.springframework.test.context.support.DelegatingSmartContextLoader=INFO #log4j.logger.org.springframework.test.context.support.AbstractGenericContextLoader=INFO diff --git a/spring-test/src/test/resources/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests-context.xml b/spring-test/src/test/resources/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests-context.xml index 67590a6b58..06ed319a09 100644 --- a/spring-test/src/test/resources/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests-context.xml +++ b/spring-test/src/test/resources/org/springframework/test/context/junit4/ParameterizedDependencyInjectionTests-context.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> diff --git a/spring-test/src/test/resources/org/springframework/test/context/junit4/transactionalTests-context.xml b/spring-test/src/test/resources/org/springframework/test/context/junit4/transactionalTests-context.xml index 01a7cf9f8c..728b423b0e 100644 --- a/spring-test/src/test/resources/org/springframework/test/context/junit4/transactionalTests-context.xml +++ b/spring-test/src/test/resources/org/springframework/test/context/junit4/transactionalTests-context.xml @@ -1,16 +1,14 @@ + xmlns:p="http://www.springframework.org/schema/p" xmlns:jdbc="http://www.springframework.org/schema/jdbc" + xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.2.xsd + http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> - - - + + + p:dataSource-ref="dataSource" />