diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/InternalSpringJUnit4ClassRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/InternalSpringJUnit4ClassRunner.java new file mode 100644 index 000000000000..61aca14e7735 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/InternalSpringJUnit4ClassRunner.java @@ -0,0 +1,447 @@ +/* + * Copyright 2002-2013 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; + +import java.lang.reflect.Method; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.internal.AssumptionViolatedException; +import org.junit.internal.runners.model.EachTestNotifier; +import org.junit.internal.runners.model.ReflectiveCallable; +import org.junit.internal.runners.statements.ExpectException; +import org.junit.internal.runners.statements.Fail; +import org.junit.internal.runners.statements.FailOnTimeout; +import org.junit.runner.Description; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.springframework.test.annotation.ExpectedException; +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.statements.RunAfterTestClassCallbacks; +import org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks; +import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks; +import org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks; +import org.springframework.test.context.junit4.statements.SpringFailOnTimeout; +import org.springframework.test.context.junit4.statements.SpringRepeat; +import org.springframework.util.ReflectionUtils; + +/** + *

+ * {@code InternalSpringJUnit4ClassRunner} is where the real stuff happens. + * {@link SpringJUnit4ClassRunner} delegates either directly, or indirectly + * through {@link SpringJUnit4ParameterizedClassRunner}, to this class. + * + * @author Sam Brannen + * @author Juergen Hoeller + * @since 4.0 + * @see SpringJUnit4ClassRunner + */ +@SuppressWarnings("deprecation") +class InternalSpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { + + private static final Log logger = LogFactory.getLog(InternalSpringJUnit4ClassRunner.class); + + private final TestContextManager testContextManager; + + + /** + * Constructs a new {@code InternalSpringJUnit4ClassRunner} and initializes a + * {@link TestContextManager} to provide Spring testing functionality to + * standard JUnit tests. + * @param clazz the test class to be run + * @see #createTestContextManager(Class) + */ + public InternalSpringJUnit4ClassRunner(Class clazz) throws InitializationError { + super(clazz); + if (logger.isDebugEnabled()) { + logger.debug("SpringJUnit4ClassRunner constructor called with [" + clazz + "]."); + } + this.testContextManager = createTestContextManager(clazz); + } + + /** + * Creates a new {@link TestContextManager} for the supplied test class and + * the configured default {@code ContextLoader} class name. + * Can be overridden by subclasses. + * @param clazz the test class to be managed + * @see #getDefaultContextLoaderClassName(Class) + */ + protected TestContextManager createTestContextManager(Class clazz) { + return new TestContextManager(clazz, getDefaultContextLoaderClassName(clazz)); + } + + /** + * Get the {@link TestContextManager} associated with this runner. + */ + protected final TestContextManager getTestContextManager() { + return this.testContextManager; + } + + /** + * Get the name of the default {@code ContextLoader} class to use for + * the supplied test class. The named class will be used if the test class + * does not explicitly declare a {@code ContextLoader} class via the + * {@code @ContextConfiguration} annotation. + *

The default implementation returns {@code null}, thus implying use + * of the standard default {@code ContextLoader} class name. + * Can be overridden by subclasses. + * @param clazz the test class + * @return {@code null} + */ + protected String getDefaultContextLoaderClassName(Class clazz) { + return null; + } + + /** + * Returns a description suitable for an ignored test class if the test is + * disabled via {@code @IfProfileValue} at the class-level, and + * otherwise delegates to the parent implementation. + * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class) + */ + @Override + public Description getDescription() { + if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) { + return Description.createSuiteDescription(getTestClass().getJavaClass()); + } + return super.getDescription(); + } + + /** + * Check whether the test is enabled in the first place. This prevents + * classes with a non-matching {@code @IfProfileValue} annotation + * from running altogether, even skipping the execution of + * {@code prepareTestInstance()} {@code TestExecutionListener} + * methods. + * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class) + * @see org.springframework.test.annotation.IfProfileValue + * @see org.springframework.test.context.TestExecutionListener + */ + @Override + public void run(RunNotifier notifier) { + if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) { + notifier.fireTestIgnored(getDescription()); + return; + } + super.run(notifier); + } + + /** + * Wraps the {@link Statement} returned by the parent implementation with a + * {@link RunBeforeTestClassCallbacks} statement, thus preserving the + * default functionality but adding support for the Spring TestContext + * Framework. + * @see RunBeforeTestClassCallbacks + */ + @Override + protected Statement withBeforeClasses(Statement statement) { + Statement junitBeforeClasses = super.withBeforeClasses(statement); + return new RunBeforeTestClassCallbacks(junitBeforeClasses, getTestContextManager()); + } + + /** + * Wraps the {@link Statement} returned by the parent implementation with a + * {@link RunAfterTestClassCallbacks} statement, thus preserving the default + * functionality but adding support for the Spring TestContext Framework. + * @see RunAfterTestClassCallbacks + */ + @Override + protected Statement withAfterClasses(Statement statement) { + Statement junitAfterClasses = super.withAfterClasses(statement); + return new RunAfterTestClassCallbacks(junitAfterClasses, getTestContextManager()); + } + + /** + * Delegates to the parent implementation for creating the test instance and + * then allows the {@link #getTestContextManager() TestContextManager} to + * prepare the test instance before returning it. + * @see TestContextManager#prepareTestInstance(Object) + */ + @Override + protected Object createTest() throws Exception { + Object testInstance = super.createTest(); + getTestContextManager().prepareTestInstance(testInstance); + return testInstance; + } + + /** + * Performs the same logic as + * {@link BlockJUnit4ClassRunner#runChild(FrameworkMethod, RunNotifier)}, + * except that tests are determined to be ignored by + * {@link #isTestMethodIgnored(FrameworkMethod)}. + */ + @Override + protected void runChild(FrameworkMethod frameworkMethod, RunNotifier notifier) { + EachTestNotifier eachNotifier = springMakeNotifier(frameworkMethod, notifier); + if (isTestMethodIgnored(frameworkMethod)) { + eachNotifier.fireTestIgnored(); + return; + } + + eachNotifier.fireTestStarted(); + try { + methodBlock(frameworkMethod).evaluate(); + } + catch (AssumptionViolatedException e) { + eachNotifier.addFailedAssumption(e); + } + catch (Throwable e) { + eachNotifier.addFailure(e); + } + finally { + eachNotifier.fireTestFinished(); + } + } + + /** + * {@code springMakeNotifier()} is an exact copy of + * {@link BlockJUnit4ClassRunner BlockJUnit4ClassRunner's} + * {@code makeNotifier()} method, but we have decided to prefix it with + * "spring" and keep it {@code private} in order to avoid the + * compatibility clashes that were introduced in JUnit between versions 4.5, + * 4.6, and 4.7. + */ + private EachTestNotifier springMakeNotifier(FrameworkMethod method, RunNotifier notifier) { + Description description = describeChild(method); + return new EachTestNotifier(notifier, description); + } + + /** + * Augments the default JUnit behavior + * {@link #withPotentialRepeat(FrameworkMethod, Object, Statement) with + * potential repeats} of the entire execution chain. + *

Furthermore, support for timeouts has been moved down the execution chain + * in order to include execution of {@link org.junit.Before @Before} + * and {@link org.junit.After @After} methods within the timed + * execution. Note that this differs from the default JUnit behavior of + * executing {@code @Before} and {@code @After} methods + * in the main thread while executing the actual test method in a separate + * thread. Thus, the end effect is that {@code @Before} and + * {@code @After} methods will be executed in the same thread as + * the test method. As a consequence, JUnit-specified timeouts will work + * fine in combination with Spring transactions. Note that JUnit-specific + * timeouts still differ from Spring-specific timeouts in that the former + * execute in a separate thread while the latter simply execute in the main + * thread (like regular tests). + * @see #possiblyExpectingExceptions(FrameworkMethod, Object, Statement) + * @see #withBefores(FrameworkMethod, Object, Statement) + * @see #withAfters(FrameworkMethod, Object, Statement) + * @see #withPotentialTimeout(FrameworkMethod, Object, Statement) + * @see #withPotentialRepeat(FrameworkMethod, Object, Statement) + */ + @Override + protected Statement methodBlock(FrameworkMethod frameworkMethod) { + Object testInstance; + try { + testInstance = new ReflectiveCallable() { + + @Override + protected Object runReflectiveCall() throws Throwable { + return createTest(); + } + }.run(); + } + catch (Throwable ex) { + return new Fail(ex); + } + + Statement statement = methodInvoker(frameworkMethod, testInstance); + statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement); + statement = withBefores(frameworkMethod, testInstance, statement); + statement = withAfters(frameworkMethod, testInstance, statement); + statement = withRulesReflectively(frameworkMethod, testInstance, statement); + statement = withPotentialRepeat(frameworkMethod, testInstance, statement); + statement = withPotentialTimeout(frameworkMethod, testInstance, statement); + + return statement; + } + + /** + * Invokes JUnit 4.7's private {@code withRules()} method using + * reflection. This is necessary for backwards compatibility with the JUnit + * 4.5 and 4.6 implementations of {@link BlockJUnit4ClassRunner}. + */ + private Statement withRulesReflectively(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + Method withRulesMethod = ReflectionUtils.findMethod(getClass(), "withRules", FrameworkMethod.class, + Object.class, Statement.class); + if (withRulesMethod != null) { + // Original JUnit 4.7 code: + // statement = withRules(frameworkMethod, testInstance, statement); + ReflectionUtils.makeAccessible(withRulesMethod); + statement = (Statement) ReflectionUtils.invokeMethod(withRulesMethod, this, frameworkMethod, testInstance, + statement); + } + return statement; + } + + /** + * Returns {@code true} if {@link Ignore @Ignore} is present for + * the supplied {@link FrameworkMethod test method} or if the test method is + * disabled via {@code @IfProfileValue}. + * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Method, Class) + */ + protected boolean isTestMethodIgnored(FrameworkMethod frameworkMethod) { + Method method = frameworkMethod.getMethod(); + return (method.isAnnotationPresent(Ignore.class) || !ProfileValueUtils.isTestEnabledInThisEnvironment(method, + getTestClass().getJavaClass())); + } + + /** + * Performs the same logic as + * {@link BlockJUnit4ClassRunner#possiblyExpectingExceptions(FrameworkMethod, Object, Statement)} + * except that the expected exception is retrieved using + * {@link #getExpectedException(FrameworkMethod)}. + */ + @Override + protected Statement possiblyExpectingExceptions(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { + Class expectedException = getExpectedException(frameworkMethod); + return expectedException != null ? new ExpectException(next, expectedException) : next; + } + + /** + * Get the {@code exception} that the supplied {@link FrameworkMethod + * test method} is expected to throw. + *

Supports both Spring's {@link ExpectedException @ExpectedException(...)} + * and JUnit's {@link Test#expected() @Test(expected=...)} annotations, but + * not both simultaneously. + * @return the expected exception, or {@code null} if none was specified + */ + protected Class getExpectedException(FrameworkMethod frameworkMethod) { + Test testAnnotation = frameworkMethod.getAnnotation(Test.class); + Class junitExpectedException = (testAnnotation != null + && testAnnotation.expected() != Test.None.class ? testAnnotation.expected() : null); + + ExpectedException expectedExAnn = frameworkMethod.getAnnotation(ExpectedException.class); + Class springExpectedException = (expectedExAnn != null ? expectedExAnn.value() : null); + + if (springExpectedException != null && junitExpectedException != null) { + String msg = "Test method [" + frameworkMethod.getMethod() + + "] has been configured with Spring's @ExpectedException(" + springExpectedException.getName() + + ".class) and JUnit's @Test(expected=" + junitExpectedException.getName() + + ".class) annotations. " + + "Only one declaration of an 'expected exception' is permitted per test method."; + logger.error(msg); + throw new IllegalStateException(msg); + } + + return springExpectedException != null ? springExpectedException : junitExpectedException; + } + + /** + * Supports both Spring's {@link Timed @Timed} and JUnit's + * {@link Test#timeout() @Test(timeout=...)} annotations, but not both + * simultaneously. Returns either a {@link SpringFailOnTimeout}, a + * {@link FailOnTimeout}, or the unmodified, supplied {@link Statement} as + * appropriate. + * @see #getSpringTimeout(FrameworkMethod) + * @see #getJUnitTimeout(FrameworkMethod) + */ + @Override + protected Statement withPotentialTimeout(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { + Statement statement = null; + long springTimeout = getSpringTimeout(frameworkMethod); + long junitTimeout = getJUnitTimeout(frameworkMethod); + if (springTimeout > 0 && junitTimeout > 0) { + String msg = "Test method [" + frameworkMethod.getMethod() + + "] has been configured with Spring's @Timed(millis=" + springTimeout + + ") and JUnit's @Test(timeout=" + junitTimeout + + ") annotations. Only one declaration of a 'timeout' is permitted per test method."; + logger.error(msg); + throw new IllegalStateException(msg); + } + else if (springTimeout > 0) { + statement = new SpringFailOnTimeout(next, springTimeout); + } + else if (junitTimeout > 0) { + statement = new FailOnTimeout(next, junitTimeout); + } + else { + statement = next; + } + + return statement; + } + + /** + * Retrieves the configured JUnit {@code timeout} from the {@link Test + * @Test} annotation on the supplied {@link FrameworkMethod test method}. + * @return the timeout, or {@code 0} if none was specified. + */ + protected long getJUnitTimeout(FrameworkMethod frameworkMethod) { + Test testAnnotation = frameworkMethod.getAnnotation(Test.class); + return (testAnnotation != null && testAnnotation.timeout() > 0 ? testAnnotation.timeout() : 0); + } + + /** + * Retrieves the configured Spring-specific {@code timeout} from the + * {@link Timed @Timed} annotation on the supplied + * {@link FrameworkMethod test method}. + * @return the timeout, or {@code 0} if none was specified. + */ + protected long getSpringTimeout(FrameworkMethod frameworkMethod) { + Timed timedAnnotation = frameworkMethod.getAnnotation(Timed.class); + return (timedAnnotation != null && timedAnnotation.millis() > 0 ? timedAnnotation.millis() : 0); + } + + /** + * Wraps the {@link Statement} returned by the parent implementation with a + * {@link RunBeforeTestMethodCallbacks} statement, thus preserving the + * default functionality but adding support for the Spring TestContext + * Framework. + * @see RunBeforeTestMethodCallbacks + */ + @Override + protected Statement withBefores(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + Statement junitBefores = super.withBefores(frameworkMethod, testInstance, statement); + return new RunBeforeTestMethodCallbacks(junitBefores, testInstance, frameworkMethod.getMethod(), + getTestContextManager()); + } + + /** + * Wraps the {@link Statement} returned by the parent implementation with a + * {@link RunAfterTestMethodCallbacks} statement, thus preserving the + * default functionality but adding support for the Spring TestContext + * Framework. + * @see RunAfterTestMethodCallbacks + */ + @Override + protected Statement withAfters(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + Statement junitAfters = super.withAfters(frameworkMethod, testInstance, statement); + return new RunAfterTestMethodCallbacks(junitAfters, testInstance, frameworkMethod.getMethod(), + getTestContextManager()); + } + + /** + * Supports Spring's {@link Repeat @Repeat} annotation by returning a + * {@link SpringRepeat} statement initialized with the configured repeat + * count or {@code 1} if no repeat count is configured. + * @see SpringRepeat + */ + protected Statement withPotentialRepeat(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { + Repeat repeatAnnotation = frameworkMethod.getAnnotation(Repeat.class); + int repeat = (repeatAnnotation != null ? repeatAnnotation.value() : 1); + return new SpringRepeat(next, frameworkMethod.getMethod(), repeat); + } + +} 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 5496b4ee6330..102628fab561 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 @@ -1,11 +1,11 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 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 + * 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, @@ -16,48 +16,39 @@ package org.springframework.test.context.junit4; +import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.junit.Ignore; import org.junit.Test; -import org.junit.internal.AssumptionViolatedException; -import org.junit.internal.runners.model.EachTestNotifier; -import org.junit.internal.runners.model.ReflectiveCallable; -import org.junit.internal.runners.statements.ExpectException; -import org.junit.internal.runners.statements.Fail; -import org.junit.internal.runners.statements.FailOnTimeout; import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.manipulation.Sortable; +import org.junit.runner.manipulation.Sorter; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.Parameterized.Parameters; import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; import org.springframework.test.annotation.ExpectedException; -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.statements.RunAfterTestClassCallbacks; -import org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks; -import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks; -import org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks; -import org.springframework.test.context.junit4.statements.SpringFailOnTimeout; -import org.springframework.test.context.junit4.statements.SpringRepeat; -import org.springframework.util.ReflectionUtils; /** *

- * {@code SpringJUnit4ClassRunner} is a custom extension of - * {@link BlockJUnit4ClassRunner} which provides functionality of the - * Spring TestContext Framework to standard JUnit 4.5+ tests by means - * of the {@link TestContextManager} and associated support classes and - * annotations. + * {@code SpringJUnit4ClassRunner} is a custom extension of {@link BlockJUnit4ClassRunner} + * which provides functionality of the Spring TestContext Framework to standard + * JUnit 4.5+ tests by means of the {@link TestContextManager} and associated support + * classes and annotations. *

*

- * The following list constitutes all annotations currently supported directly - * by {@code SpringJUnit4ClassRunner}. + * The following list constitutes all annotations currently supported directly by + * {@code SpringJUnit4ClassRunner}. * (Note that additional annotations may be supported by various * {@link org.springframework.test.context.TestExecutionListener * TestExecutionListeners}) @@ -69,406 +60,103 @@ *

  • {@link Timed @Timed}
  • *
  • {@link Repeat @Repeat}
  • *
  • {@link Ignore @Ignore}
  • + *
  • {@link Parameters @Parameters}
  • *
  • * {@link org.springframework.test.annotation.ProfileValueSourceConfiguration * @ProfileValueSourceConfiguration}
  • - *
  • {@link org.springframework.test.annotation.IfProfileValue - * @IfProfileValue}
  • + *
  • {@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}
  • * *

    - * NOTE: As of Spring 3.0, {@code SpringJUnit4ClassRunner} requires - * JUnit 4.5+. + * NOTE: As of Spring 3.0, {@code SpringJUnit4ClassRunner} requires JUnit 4.5+. *

    - * + * * @author Sam Brannen * @author Juergen Hoeller + * @author Gaetan Pitteloud * @since 2.5 * @see TestContextManager */ -@SuppressWarnings("deprecation") -public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { - - private static final Log logger = LogFactory.getLog(SpringJUnit4ClassRunner.class); +public class SpringJUnit4ClassRunner extends Runner implements Filterable, Sortable { - private final TestContextManager testContextManager; + private final Runner internalRunner; - - /** - * Constructs a new {@code SpringJUnit4ClassRunner} and initializes a - * {@link TestContextManager} to provide Spring testing functionality to - * standard JUnit tests. - * @param clazz the test class to be run - * @see #createTestContextManager(Class) - */ - public SpringJUnit4ClassRunner(Class clazz) throws InitializationError { - super(clazz); - if (logger.isDebugEnabled()) { - logger.debug("SpringJUnit4ClassRunner constructor called with [" + clazz + "]."); + public SpringJUnit4ClassRunner(Class testClass) throws InitializationError { + List parametersMethods = findParametersMethods(testClass); + int nbMethods = parametersMethods.size(); + switch (nbMethods) { + case 0: + internalRunner = new InternalSpringJUnit4ClassRunner(testClass); + break; + case 1: + internalRunner = new SpringJUnit4ParameterizedClassRunner(testClass, + parametersMethods.get(0)); + break; + default: + throw new InitializationError(String.format( + "More than one @Parameters method found on %s", testClass)); } - this.testContextManager = createTestContextManager(clazz); - } - - /** - * Creates a new {@link TestContextManager} for the supplied test class and - * the configured default {@code ContextLoader} class name. - * Can be overridden by subclasses. - * @param clazz the test class to be managed - * @see #getDefaultContextLoaderClassName(Class) - */ - protected TestContextManager createTestContextManager(Class clazz) { - return new TestContextManager(clazz, getDefaultContextLoaderClassName(clazz)); - } - - /** - * Get the {@link TestContextManager} associated with this runner. - */ - protected final TestContextManager getTestContextManager() { - return this.testContextManager; } - /** - * Get the name of the default {@code ContextLoader} class to use for - * the supplied test class. The named class will be used if the test class - * does not explicitly declare a {@code ContextLoader} class via the - * {@code @ContextConfiguration} annotation. - *

    The default implementation returns {@code null}, thus implying use - * of the standard default {@code ContextLoader} class name. - * Can be overridden by subclasses. - * @param clazz the test class - * @return {@code null} - */ - protected String getDefaultContextLoaderClassName(Class clazz) { - return null; - } - - /** - * Returns a description suitable for an ignored test class if the test is - * disabled via {@code @IfProfileValue} at the class-level, and - * otherwise delegates to the parent implementation. - * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class) - */ @Override public Description getDescription() { - if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) { - return Description.createSuiteDescription(getTestClass().getJavaClass()); - } - return super.getDescription(); + return internalRunner.getDescription(); } - /** - * Check whether the test is enabled in the first place. This prevents - * classes with a non-matching {@code @IfProfileValue} annotation - * from running altogether, even skipping the execution of - * {@code prepareTestInstance()} {@code TestExecutionListener} - * methods. - * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class) - * @see org.springframework.test.annotation.IfProfileValue - * @see org.springframework.test.context.TestExecutionListener - */ @Override public void run(RunNotifier notifier) { - if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) { - notifier.fireTestIgnored(getDescription()); - return; - } - super.run(notifier); - } - - /** - * Wraps the {@link Statement} returned by the parent implementation with a - * {@link RunBeforeTestClassCallbacks} statement, thus preserving the - * default functionality but adding support for the Spring TestContext - * Framework. - * @see RunBeforeTestClassCallbacks - */ - @Override - protected Statement withBeforeClasses(Statement statement) { - Statement junitBeforeClasses = super.withBeforeClasses(statement); - return new RunBeforeTestClassCallbacks(junitBeforeClasses, getTestContextManager()); - } - - /** - * Wraps the {@link Statement} returned by the parent implementation with a - * {@link RunAfterTestClassCallbacks} statement, thus preserving the default - * functionality but adding support for the Spring TestContext Framework. - * @see RunAfterTestClassCallbacks - */ - @Override - protected Statement withAfterClasses(Statement statement) { - Statement junitAfterClasses = super.withAfterClasses(statement); - return new RunAfterTestClassCallbacks(junitAfterClasses, getTestContextManager()); + internalRunner.run(notifier); } - /** - * Delegates to the parent implementation for creating the test instance and - * then allows the {@link #getTestContextManager() TestContextManager} to - * prepare the test instance before returning it. - * @see TestContextManager#prepareTestInstance(Object) - */ - @Override - protected Object createTest() throws Exception { - Object testInstance = super.createTest(); - getTestContextManager().prepareTestInstance(testInstance); - return testInstance; - } - - /** - * Performs the same logic as - * {@link BlockJUnit4ClassRunner#runChild(FrameworkMethod, RunNotifier)}, - * except that tests are determined to be ignored by - * {@link #isTestMethodIgnored(FrameworkMethod)}. - */ - @Override - protected void runChild(FrameworkMethod frameworkMethod, RunNotifier notifier) { - EachTestNotifier eachNotifier = springMakeNotifier(frameworkMethod, notifier); - if (isTestMethodIgnored(frameworkMethod)) { - eachNotifier.fireTestIgnored(); - return; - } - - eachNotifier.fireTestStarted(); - try { - methodBlock(frameworkMethod).evaluate(); - } - catch (AssumptionViolatedException e) { - eachNotifier.addFailedAssumption(e); - } - catch (Throwable e) { - eachNotifier.addFailure(e); - } - finally { - eachNotifier.fireTestFinished(); - } + public void filter(Filter filter) throws NoTestsRemainException { + ((Filterable) internalRunner).filter(filter); } - /** - * {@code springMakeNotifier()} is an exact copy of - * {@link BlockJUnit4ClassRunner BlockJUnit4ClassRunner's} - * {@code makeNotifier()} method, but we have decided to prefix it with - * "spring" and keep it {@code private} in order to avoid the - * compatibility clashes that were introduced in JUnit between versions 4.5, - * 4.6, and 4.7. - */ - private EachTestNotifier springMakeNotifier(FrameworkMethod method, RunNotifier notifier) { - Description description = describeChild(method); - return new EachTestNotifier(notifier, description); + public void sort(Sorter sorter) { + ((Sortable) internalRunner).sort(sorter); } /** - * Augments the default JUnit behavior - * {@link #withPotentialRepeat(FrameworkMethod, Object, Statement) with - * potential repeats} of the entire execution chain. - *

    Furthermore, support for timeouts has been moved down the execution chain - * in order to include execution of {@link org.junit.Before @Before} - * and {@link org.junit.After @After} methods within the timed - * execution. Note that this differs from the default JUnit behavior of - * executing {@code @Before} and {@code @After} methods - * in the main thread while executing the actual test method in a separate - * thread. Thus, the end effect is that {@code @Before} and - * {@code @After} methods will be executed in the same thread as - * the test method. As a consequence, JUnit-specified timeouts will work - * fine in combination with Spring transactions. Note that JUnit-specific - * timeouts still differ from Spring-specific timeouts in that the former - * execute in a separate thread while the latter simply execute in the main - * thread (like regular tests). - * @see #possiblyExpectingExceptions(FrameworkMethod, Object, Statement) - * @see #withBefores(FrameworkMethod, Object, Statement) - * @see #withAfters(FrameworkMethod, Object, Statement) - * @see #withPotentialTimeout(FrameworkMethod, Object, Statement) - * @see #withPotentialRepeat(FrameworkMethod, Object, Statement) + * Gets all methods in the supplied {@link Class class} and its superclasses which are + * annotated with {@link Parameters}. + * + * @param clazz the class for which to retrieve the annotated methods + * @return all annotated methods in the supplied class and its superclasses */ - @Override - protected Statement methodBlock(FrameworkMethod frameworkMethod) { - Object testInstance; - try { - testInstance = new ReflectiveCallable() { - - @Override - protected Object runReflectiveCall() throws Throwable { - return createTest(); + private List findParametersMethods(Class clazz) { + Class annotationType = Parameters.class; + List results = new ArrayList(); + for (Class eachClass : getSuperClasses(clazz)) { + Method[] methods = eachClass.getDeclaredMethods(); + for (Method eachMethod : methods) { + Annotation annotation = eachMethod.getAnnotation(annotationType); + if (annotation != null) { + // no need to check for overridden/shadowed methods: + // the method must be static (checked later) + results.add(eachMethod); } - }.run(); - } - catch (Throwable ex) { - return new Fail(ex); + } } - - Statement statement = methodInvoker(frameworkMethod, testInstance); - statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement); - statement = withBefores(frameworkMethod, testInstance, statement); - statement = withAfters(frameworkMethod, testInstance, statement); - statement = withRulesReflectively(frameworkMethod, testInstance, statement); - statement = withPotentialRepeat(frameworkMethod, testInstance, statement); - statement = withPotentialTimeout(frameworkMethod, testInstance, statement); - - return statement; - } - - /** - * Invokes JUnit 4.7's private {@code withRules()} method using - * reflection. This is necessary for backwards compatibility with the JUnit - * 4.5 and 4.6 implementations of {@link BlockJUnit4ClassRunner}. - */ - private Statement withRulesReflectively(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { - Method withRulesMethod = ReflectionUtils.findMethod(getClass(), "withRules", FrameworkMethod.class, - Object.class, Statement.class); - if (withRulesMethod != null) { - // Original JUnit 4.7 code: - // statement = withRules(frameworkMethod, testInstance, statement); - ReflectionUtils.makeAccessible(withRulesMethod); - statement = (Statement) ReflectionUtils.invokeMethod(withRulesMethod, this, frameworkMethod, testInstance, - statement); - } - return statement; - } - - /** - * Returns {@code true} if {@link Ignore @Ignore} is present for - * the supplied {@link FrameworkMethod test method} or if the test method is - * disabled via {@code @IfProfileValue}. - * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Method, Class) - */ - protected boolean isTestMethodIgnored(FrameworkMethod frameworkMethod) { - Method method = frameworkMethod.getMethod(); - return (method.isAnnotationPresent(Ignore.class) || !ProfileValueUtils.isTestEnabledInThisEnvironment(method, - getTestClass().getJavaClass())); - } - - /** - * Performs the same logic as - * {@link BlockJUnit4ClassRunner#possiblyExpectingExceptions(FrameworkMethod, Object, Statement)} - * except that the expected exception is retrieved using - * {@link #getExpectedException(FrameworkMethod)}. - */ - @Override - protected Statement possiblyExpectingExceptions(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { - Class expectedException = getExpectedException(frameworkMethod); - return expectedException != null ? new ExpectException(next, expectedException) : next; + return results; } /** - * Get the {@code exception} that the supplied {@link FrameworkMethod - * test method} is expected to throw. - *

    Supports both Spring's {@link ExpectedException @ExpectedException(...)} - * and JUnit's {@link Test#expected() @Test(expected=...)} annotations, but - * not both simultaneously. - * @return the expected exception, or {@code null} if none was specified + * Gets all superclasses of the supplied {@link Class class}, including the class + * itself. The ordering of the returned list will begin with the supplied class and + * continue up the class hierarchy. + *

    + * Note: This code has been borrowed from + * {@link org.junit.internal.runners.TestClass#getSuperClasses(Class)} and adapted. + * + * @param clazz the class for which to retrieve the superclasses. + * @return all superclasses of the supplied class. */ - protected Class getExpectedException(FrameworkMethod frameworkMethod) { - Test testAnnotation = frameworkMethod.getAnnotation(Test.class); - Class junitExpectedException = (testAnnotation != null - && testAnnotation.expected() != Test.None.class ? testAnnotation.expected() : null); - - ExpectedException expectedExAnn = frameworkMethod.getAnnotation(ExpectedException.class); - Class springExpectedException = (expectedExAnn != null ? expectedExAnn.value() : null); - - if (springExpectedException != null && junitExpectedException != null) { - String msg = "Test method [" + frameworkMethod.getMethod() - + "] has been configured with Spring's @ExpectedException(" + springExpectedException.getName() - + ".class) and JUnit's @Test(expected=" + junitExpectedException.getName() - + ".class) annotations. " - + "Only one declaration of an 'expected exception' is permitted per test method."; - logger.error(msg); - throw new IllegalStateException(msg); - } - - return springExpectedException != null ? springExpectedException : junitExpectedException; - } - - /** - * Supports both Spring's {@link Timed @Timed} and JUnit's - * {@link Test#timeout() @Test(timeout=...)} annotations, but not both - * simultaneously. Returns either a {@link SpringFailOnTimeout}, a - * {@link FailOnTimeout}, or the unmodified, supplied {@link Statement} as - * appropriate. - * @see #getSpringTimeout(FrameworkMethod) - * @see #getJUnitTimeout(FrameworkMethod) - */ - @Override - protected Statement withPotentialTimeout(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { - Statement statement = null; - long springTimeout = getSpringTimeout(frameworkMethod); - long junitTimeout = getJUnitTimeout(frameworkMethod); - if (springTimeout > 0 && junitTimeout > 0) { - String msg = "Test method [" + frameworkMethod.getMethod() - + "] has been configured with Spring's @Timed(millis=" + springTimeout - + ") and JUnit's @Test(timeout=" + junitTimeout - + ") annotations. Only one declaration of a 'timeout' is permitted per test method."; - logger.error(msg); - throw new IllegalStateException(msg); - } - else if (springTimeout > 0) { - statement = new SpringFailOnTimeout(next, springTimeout); + private List> getSuperClasses(Class clazz) { + ArrayList> results = new ArrayList>(); + Class current = clazz; + while (current != null) { + results.add(current); + current = current.getSuperclass(); } - else if (junitTimeout > 0) { - statement = new FailOnTimeout(next, junitTimeout); - } - else { - statement = next; - } - - return statement; - } - - /** - * Retrieves the configured JUnit {@code timeout} from the {@link Test - * @Test} annotation on the supplied {@link FrameworkMethod test method}. - * @return the timeout, or {@code 0} if none was specified. - */ - protected long getJUnitTimeout(FrameworkMethod frameworkMethod) { - Test testAnnotation = frameworkMethod.getAnnotation(Test.class); - return (testAnnotation != null && testAnnotation.timeout() > 0 ? testAnnotation.timeout() : 0); - } - - /** - * Retrieves the configured Spring-specific {@code timeout} from the - * {@link Timed @Timed} annotation on the supplied - * {@link FrameworkMethod test method}. - * @return the timeout, or {@code 0} if none was specified. - */ - protected long getSpringTimeout(FrameworkMethod frameworkMethod) { - Timed timedAnnotation = frameworkMethod.getAnnotation(Timed.class); - return (timedAnnotation != null && timedAnnotation.millis() > 0 ? timedAnnotation.millis() : 0); - } - - /** - * Wraps the {@link Statement} returned by the parent implementation with a - * {@link RunBeforeTestMethodCallbacks} statement, thus preserving the - * default functionality but adding support for the Spring TestContext - * Framework. - * @see RunBeforeTestMethodCallbacks - */ - @Override - protected Statement withBefores(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { - Statement junitBefores = super.withBefores(frameworkMethod, testInstance, statement); - return new RunBeforeTestMethodCallbacks(junitBefores, testInstance, frameworkMethod.getMethod(), - getTestContextManager()); + return results; } - - /** - * Wraps the {@link Statement} returned by the parent implementation with a - * {@link RunAfterTestMethodCallbacks} statement, thus preserving the - * default functionality but adding support for the Spring TestContext - * Framework. - * @see RunAfterTestMethodCallbacks - */ - @Override - protected Statement withAfters(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { - Statement junitAfters = super.withAfters(frameworkMethod, testInstance, statement); - return new RunAfterTestMethodCallbacks(junitAfters, testInstance, frameworkMethod.getMethod(), - getTestContextManager()); - } - - /** - * Supports Spring's {@link Repeat @Repeat} annotation by returning a - * {@link SpringRepeat} statement initialized with the configured repeat - * count or {@code 1} if no repeat count is configured. - * @see SpringRepeat - */ - protected Statement withPotentialRepeat(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { - Repeat repeatAnnotation = frameworkMethod.getAnnotation(Repeat.class); - int repeat = (repeatAnnotation != null ? repeatAnnotation.value() : 1); - return new SpringRepeat(next, frameworkMethod.getMethod(), repeat); - } - } diff --git a/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ParameterizedClassRunner.java b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ParameterizedClassRunner.java new file mode 100644 index 000000000000..4b837d21ba3f --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ParameterizedClassRunner.java @@ -0,0 +1,188 @@ +/* + * Copyright 2002-2013 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; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.Suite; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.springframework.test.annotation.ProfileValueUtils; +import org.springframework.test.context.TestContextManager; +import org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks; +import org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks; + +/** + * This runner executes each test method many times, with before/after class methods + * executed once around. + *

    + * Borrowed from JUnit's {@code Parameterized} runner, and adapted to be able to run the + * test with a spring-aware runner. In particular, the TestContextManager is created once + * by this class, in order to be able to invoke beforeTestClass and afterTestClass methods + * once for every set of parameters. + *

    + * Invoked internally by the {@link SpringJUnit4ClassRunner}. + * + * @since 3.2 + * @author Gaetan Pitteloud + */ +class SpringJUnit4ParameterizedClassRunner extends Suite { + + private final ArrayList runners = new ArrayList(); + + TestContextManager testContextManager; + + /** + * Copy-paste from Parameterized inner class, adapted for spring test-context + * framework. + */ + class TestClassRunnerForParameters extends InternalSpringJUnit4ClassRunner { + + private final Object[] fParameters; + + private final int fParameterSetNumber; + + TestClassRunnerForParameters(Class testClass, Object[] parameters, int i) + throws InitializationError { + super(testClass); + fParameters = parameters; + fParameterSetNumber = i; + } + + @Override + protected TestContextManager createTestContextManager(Class testClass) { + // already created by wrapping class + return SpringJUnit4ParameterizedClassRunner.this.testContextManager; + } + + @Override + protected Object createTest() throws Exception { + Object testInstance = getTestClass().getOnlyConstructor().newInstance(fParameters); + getTestContextManager().prepareTestInstance(testInstance); + return testInstance; + } + + @Override + protected String getName() { + return String.format("[%s]", fParameterSetNumber); + } + + @Override + protected String testName(final FrameworkMethod method) { + return String.format("%s[%s]", method.getName(), fParameterSetNumber); + } + + @Override + protected void validateConstructor(List errors) { + validateOnlyOneConstructor(errors); + } + + @Override + protected Statement classBlock(RunNotifier notifier) { + return childrenInvoker(notifier); + } + + } + + public SpringJUnit4ParameterizedClassRunner(Class klass, Method parametersMethod) + throws InitializationError { + super(klass, Collections. emptyList()); + int paramMethodModifiers = parametersMethod.getModifiers(); + if (!Modifier.isStatic(paramMethodModifiers) || !Modifier.isPublic(paramMethodModifiers)) { + throw new InitializationError(String.format( + "%s.%s() must be public and static", getTestClass().getName(), + parametersMethod.getName())); + } + + // do not collect parameters nor create manager as the test class will be skipped + if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) { + return; + } + + testContextManager = new TestContextManager(klass); + Collection parametersList = getParametersList(parametersMethod); + int i = 0; + for (Object testParameters : parametersList) { + if (testParameters instanceof Object[]) { + runners.add(new TestClassRunnerForParameters( + getTestClass().getJavaClass(), (Object[]) testParameters, i++)); + } else { + throw new InitializationError(String.format( + "%s.%s() must return a Collection of arrays.", + getTestClass().getName(), parametersMethod.getName())); + } + } + + } + + @Override + public void run(RunNotifier notifier) { + if (testContextManager == null) { + notifier.fireTestIgnored(getDescription()); + } else { + super.run(notifier); + } + } + + /** + * Wraps the {@link Statement} returned by the parent implementation with a + * {@link RunBeforeTestClassCallbacks} statement, thus preserving the default + * functionality but adding support for the Spring TestContext Framework. + * + * @see RunBeforeTestClassCallbacks + */ + @Override + protected Statement withBeforeClasses(Statement statement) { + Statement junitBeforeClasses = super.withBeforeClasses(statement); + return new RunBeforeTestClassCallbacks(junitBeforeClasses, testContextManager); + } + + /** + * Wraps the {@link Statement} returned by the parent implementation with a + * {@link RunAfterTestClassCallbacks} statement, thus preserving the default + * functionality but adding support for the Spring TestContext Framework. + * + * @see RunAfterTestClassCallbacks + */ + @Override + protected Statement withAfterClasses(Statement statement) { + Statement junitAfterClasses = super.withAfterClasses(statement); + return new RunAfterTestClassCallbacks(junitAfterClasses, testContextManager); + } + + @Override + protected List getChildren() { + return runners; + } + + private Collection getParametersList(Method parametersMethod) throws InitializationError { + try { + return (Collection) parametersMethod.invoke(null); + } catch (Exception e) { + throw new InitializationError(e); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java index eb1c39a908b8..6550b249ff6c 100644 --- a/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/ContextCacheTests.java @@ -24,9 +24,11 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader; import org.springframework.test.util.ReflectionTestUtils; -import static org.junit.Assert.*; import static org.springframework.test.context.SpringRunnerContextCacheTests.*; + +import static org.junit.Assert.*; + /** * Integration tests for verifying proper behavior of the {@link ContextCache} in * conjunction with cache keys used in {@link TestContext}. diff --git a/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java b/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java index 07f7383189cc..627a44396701 100644 --- a/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/SpringRunnerContextCacheTests.java @@ -18,19 +18,19 @@ import static org.junit.Assert.*; -import java.util.Comparator; -import java.util.List; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.SpringRunnerContextCacheTests.OrderedMethodsSpringJUnit4ClassRunner; +import org.springframework.test.context.ContextCache; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestContextManager; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit4.OrderedMethodsSpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; import org.springframework.test.context.support.DirtiesContextTestExecutionListener; @@ -130,31 +130,4 @@ public void verifyContextNotDirty() { SpringRunnerContextCacheTests.dirtiedApplicationContext, this.applicationContext); } - - /** - * @since 3.2 - */ - public static class OrderedMethodsSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner { - - public OrderedMethodsSpringJUnit4ClassRunner(Class clazz) throws InitializationError { - super(clazz); - } - - @Override - protected List computeTestMethods() { - List testMethods = super.computeTestMethods(); - - java.util.Collections.sort(testMethods, new Comparator() { - - @Override - public int compare(FrameworkMethod method1, FrameworkMethod method2) { - return method1.getName().compareTo(method2.getName()); - } - }); - - return testMethods; - } - - } - } diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java index 3ee77974a0e9..dfd6a298753d 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/CustomDefaultContextLoaderClassSpringRunnerTests.java @@ -56,7 +56,7 @@ public void verifyAnnotationAutowiredFields() { } - public static final class PropertiesBasedSpringJUnit4ClassRunner extends SpringJUnit4ClassRunner { + public static final class PropertiesBasedSpringJUnit4ClassRunner extends InternalSpringJUnit4ClassRunner { public PropertiesBasedSpringJUnit4ClassRunner(Class clazz) throws InitializationError { super(clazz); diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/InternalSpringJUnit4ClassRunnerTests.java similarity index 86% rename from spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerTests.java rename to spring-test/src/test/java/org/springframework/test/context/junit4/InternalSpringJUnit4ClassRunnerTests.java index aedd5ad46668..085b3a908eb3 100644 --- a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/InternalSpringJUnit4ClassRunnerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2013 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,11 +25,11 @@ * @author Sam Brannen * @since 2.5 */ -public class SpringJUnit4ClassRunnerTests { +public class InternalSpringJUnit4ClassRunnerTests { @Test(expected = Exception.class) public void checkThatExceptionsAreNotSilentlySwallowed() throws Exception { - SpringJUnit4ClassRunner runner = new SpringJUnit4ClassRunner(getClass()) { + InternalSpringJUnit4ClassRunner runner = new InternalSpringJUnit4ClassRunner(getClass()) { @Override protected TestContextManager createTestContextManager(Class clazz) { diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/OrderedMethodsSpringJUnit4ClassRunner.java b/spring-test/src/test/java/org/springframework/test/context/junit4/OrderedMethodsSpringJUnit4ClassRunner.java new file mode 100644 index 000000000000..f36d302ce74f --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/OrderedMethodsSpringJUnit4ClassRunner.java @@ -0,0 +1,50 @@ +/* + * Copyright 2002-2013 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; + +import java.util.Comparator; +import java.util.List; + +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.springframework.test.context.junit4.InternalSpringJUnit4ClassRunner; + +/** + * @since 3.2 + */ +public class OrderedMethodsSpringJUnit4ClassRunner extends InternalSpringJUnit4ClassRunner { + + public OrderedMethodsSpringJUnit4ClassRunner(Class clazz) throws InitializationError { + super(clazz); + } + + @Override + protected List computeTestMethods() { + List testMethods = super.computeTestMethods(); + + java.util.Collections.sort(testMethods, new Comparator() { + + @Override + public int compare(FrameworkMethod method1, FrameworkMethod method2) { + return method1.getName().compareTo(method2.getName()); + } + }); + + return testMethods; + } + +} \ No newline at end of file diff --git a/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerParametersTests.java b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerParametersTests.java new file mode 100644 index 000000000000..3d21b4e358e6 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunnerParametersTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2002-2013 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; + +import java.util.Arrays; +import java.util.Collection; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +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 static org.junit.Assert.*; + +/** + * Tests the behaviour of {@code Parameters} annotation in a spring test. + * + * @author Gaetan Pitteloud + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations="SpringJUnit4ClassRunnerAppCtxTests-context.xml") +public class SpringJUnit4ClassRunnerParametersTests { + + private static int beforeClassCount; + private static int testExecutionCount; + private static boolean afterClassInvoked; + + private int expected; + + private int actual; + + @Autowired + ApplicationContext context; + + @Parameters + public static Collection getParameters() { + return Arrays.asList(new Object[] { 1, 1 }, new Object[] { 2, 2 }, new Object[] {3, 3 }); + } + + public SpringJUnit4ClassRunnerParametersTests(int expected, int actual) { + this.expected = expected; + this.actual = actual; + } + + @BeforeClass public static void before() { + beforeClassCount++; + } + + @Test + public void basicTest() throws Exception { + assertEquals(expected, actual); + assertNotNull(context); + assertEquals("Foo", context.getBean("foo")); + testExecutionCount++; + } + + @AfterClass public static void sumUp() { + assertEquals(3, testExecutionCount); + // verify that beforeClass/afterClass are invoked only once + assertEquals(1, beforeClassCount); + assertFalse(afterClassInvoked); + afterClassInvoked = true; + } + +}