diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java index cb5569110ea..986600cf901 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.java @@ -16,41 +16,56 @@ package org.springframework.test.context.junit4; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.junit.internal.runners.InitializationError; -import org.junit.internal.runners.JUnit4ClassRunner; +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.Failure; 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.RunSpringTestContextAfters; +import org.springframework.test.context.junit4.statements.RunSpringTestContextBefores; +import org.springframework.test.context.junit4.statements.SpringFailOnTimeout; +import org.springframework.test.context.junit4.statements.SpringRepeat; /** *

- * SpringJUnit4ClassRunner is a custom extension of {@link JUnit4ClassRunner} - * 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. + * 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 SpringJUnit4ClassRunner. (Note that additional annotations - * may be supported by various + * by SpringJUnit4ClassRunner. + * (Note that additional annotations may be supported by various * {@link org.springframework.test.context.TestExecutionListener * TestExecutionListeners}) *

* *

- * NOTE: As of Spring 3.0 M1, SpringJUnit4ClassRunner requires JUnit 4.5, - * while internally still being based on JUnit 4.4 SPI. This will be rewritten - * based on JUnit 4.5's BlockJUnit4ClassRunner in a later Spring 3.0 release. + * NOTE: As of Spring 3.0, SpringJUnit4ClassRunner requires + * JUnit 4.5. *

* * @author Sam Brannen @@ -68,7 +82,7 @@ import org.springframework.test.context.TestContextManager; * @since 2.5 * @see TestContextManager */ -public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { +public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner { private static final Log logger = LogFactory.getLog(SpringJUnit4ClassRunner.class); @@ -91,12 +105,61 @@ public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { this.testContextManager = createTestContextManager(clazz); } + /** + * Creates a new {@link TestContextManager}. Can be overridden by + * subclasses. + * + * @param clazz the Class object corresponding to the test class to be + * managed + */ + protected TestContextManager createTestContextManager(Class clazz) { + return new TestContextManager(clazz); + } + + /** + * Get the {@link TestContextManager} associated with this runner. + */ + protected final TestContextManager getTestContextManager() { + return this.testContextManager; + } + + /** + * 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; + } + + /** + * Returns a description suitable for an ignored test class if the test is + * disabled via @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 @IfProfileValue annotation * from running altogether, even skipping the execution of - * prepareTestInstance listener methods. + * prepareTestInstance() TestExecutionListener + * methods. * + * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class) * @see org.springframework.test.annotation.IfProfileValue * @see org.springframework.test.context.TestExecutionListener */ @@ -110,71 +173,250 @@ public class SpringJUnit4ClassRunner extends JUnit4ClassRunner { } /** - * Delegates to {@link JUnit4ClassRunner#createTest()} to create the test - * instance and then to a {@link TestContextManager} to - * {@link TestContextManager#prepareTestInstance(Object) prepare} the test - * instance for Spring testing functionality. + * 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 = makeNotifier(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(); + } + } + + private EachTestNotifier makeNotifier(FrameworkMethod frameworkMethod, RunNotifier notifier) { + Description description = describeChild(frameworkMethod); + 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 @Before and @After methods + * in the main thread while executing the actual test method in a separate + * thread. Thus, the end effect is that @Before and + * @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 JUnit4ClassRunner#createTest() - * @see TestContextManager#prepareTestInstance(Object) + * @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 Object createTest() throws Exception { - Object testInstance = super.createTest(); - getTestContextManager().prepareTestInstance(testInstance); - return testInstance; + protected Statement methodBlock(FrameworkMethod frameworkMethod) { + + Object testInstance; + try { + testInstance = new ReflectiveCallable() { + + @Override + protected Object runReflectiveCall() throws Throwable { + return createTest(); + } + }.run(); + } + catch (Throwable e) { + return new Fail(e); + } + + Statement statement = methodInvoker(frameworkMethod, testInstance); + statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement); + statement = withBefores(frameworkMethod, testInstance, statement); + statement = withAfters(frameworkMethod, testInstance, statement); + statement = withPotentialTimeout(frameworkMethod, testInstance, statement); + statement = withPotentialRepeat(frameworkMethod, testInstance, statement); + + return statement; } /** - * Creates a new {@link TestContextManager}. Can be overridden by - * subclasses. + * Returns true if {@link Ignore @Ignore} is present for + * the supplied {@link FrameworkMethod test method} or if the test method is + * disabled via @IfProfileValue. * - * @param clazz the Class object corresponding to the test class to be - * managed + * @see ProfileValueUtils#isTestEnabledInThisEnvironment(Method, Class) */ - protected TestContextManager createTestContextManager(Class clazz) { - return new TestContextManager(clazz); + protected boolean isTestMethodIgnored(FrameworkMethod frameworkMethod) { + Method method = frameworkMethod.getMethod(); + return (method.isAnnotationPresent(Ignore.class) || !ProfileValueUtils.isTestEnabledInThisEnvironment(method, + getTestClass().getJavaClass())); } /** - * Get the {@link TestContextManager} associated with this runner. + * Performs the same logic as + * {@link BlockJUnit4ClassRunner#possiblyExpectingExceptions(FrameworkMethod, Object, Statement)} + * except that the expected exception is retrieved using + * {@link #getExpectedException(FrameworkMethod)}. */ - protected final TestContextManager getTestContextManager() { - return this.testContextManager; + @Override + protected Statement possiblyExpectingExceptions(FrameworkMethod frameworkMethod, Object testInstance, Statement next) { + Class expectedException = getExpectedException(frameworkMethod); + return expectedException != null ? new ExpectException(next, expectedException) : next; } /** - * Invokes the supplied {@link Method test method} and notifies the supplied - * {@link RunNotifier} of the appropriate events. + * Get the 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. + *

* - * @see #createTest() - * @see JUnit4ClassRunner#invokeTestMethod(Method,RunNotifier) + * @return the expected exception, or null if none was + * specified */ - @Override - protected void invokeTestMethod(Method method, RunNotifier notifier) { - if (logger.isDebugEnabled()) { - logger.debug("Invoking test method [" + method.toGenericString() + "]"); + 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); } - // The following is a 1-to-1 copy of the original JUnit 4.4 code, except - // that we use custom implementations for TestMethod and MethodRoadie. + return springExpectedException != null ? springExpectedException : junitExpectedException; + } - Description description = methodDescription(method); - Object testInstance; - try { - testInstance = createTest(); + /** + * 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); } - catch (InvocationTargetException ex) { - notifier.fireTestFailure(new Failure(description, ex.getCause())); - return; + else if (springTimeout > 0) { + statement = new SpringFailOnTimeout(next, springTimeout); } - catch (Exception ex) { - notifier.fireTestFailure(new Failure(description, ex)); - return; + else if (junitTimeout > 0) { + statement = new FailOnTimeout(next, junitTimeout); + } + else { + statement = next; } - SpringTestMethod testMethod = new SpringTestMethod(method, getTestClass()); - new SpringMethodRoadie(getTestContextManager(), testInstance, testMethod, notifier, description).run(); + return statement; + } + + /** + * Retrieves the configured JUnit timeout from the {@link Test + * @Test} annotation on the supplied {@link FrameworkMethod test + * method}. + * + * @return the timeout, or 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 timeout from the + * {@link Timed @Timed} annotation on the supplied + * {@link FrameworkMethod test method}. + * + * @return the timeout, or 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 RunSpringTestContextBefores} statement, thus preserving the + * default functionality but adding support for the Spring TestContext + * Framework. + * + * @see RunSpringTestContextBefores + */ + @Override + protected Statement withBefores(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + Statement junitBefores = super.withBefores(frameworkMethod, testInstance, statement); + return new RunSpringTestContextBefores(junitBefores, testInstance, frameworkMethod.getMethod(), + getTestContextManager()); + } + + /** + * Wraps the {@link Statement} returned by the parent implementation with a + * {@link RunSpringTestContextAfters} statement, thus preserving the default + * functionality but adding support for the Spring TestContext Framework. + * + * @see RunSpringTestContextAfters + */ + @Override + protected Statement withAfters(FrameworkMethod frameworkMethod, Object testInstance, Statement statement) { + Statement junitAfters = super.withAfters(frameworkMethod, testInstance, statement); + return new RunSpringTestContextAfters(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 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/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringMethodRoadie.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringMethodRoadie.java deleted file mode 100644 index 8e171b6e0d5..00000000000 --- a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringMethodRoadie.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright 2002-2008 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.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.internal.AssumptionViolatedException; -import org.junit.runner.Description; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunNotifier; - -import org.springframework.test.annotation.Repeat; -import org.springframework.test.annotation.Timed; -import org.springframework.test.context.TestContextManager; - -/** - *

- * SpringMethodRoadie is a custom implementation of JUnit 4.4's - * {@link org.junit.internal.runners.MethodRoadie MethodRoadie}, which provides - * the following enhancements: - *

- * - *

- * Due to method and field visibility constraints, the code of - * MethodRoadie has been duplicated here instead of subclassing - * MethodRoadie directly. - *

- * - * @author Sam Brannen - * @author Juergen Hoeller - * @since 2.5 - */ -class SpringMethodRoadie { - - protected static final Log logger = LogFactory.getLog(SpringMethodRoadie.class); - - private final TestContextManager testContextManager; - - private final Object testInstance; - - private final SpringTestMethod testMethod; - - private final RunNotifier notifier; - - private final Description description; - - private Throwable testException; - - - /** - * Constructs a new SpringMethodRoadie. - * @param testContextManager the TestContextManager to notify - * @param testInstance the test instance upon which to invoke the test method - * @param testMethod the test method to invoke - * @param notifier the RunNotifier to notify - * @param description the test description - */ - public SpringMethodRoadie(TestContextManager testContextManager, Object testInstance, - SpringTestMethod testMethod, RunNotifier notifier, Description description) { - - this.testContextManager = testContextManager; - this.testInstance = testInstance; - this.testMethod = testMethod; - this.notifier = notifier; - this.description = description; - } - - - /** - * Runs the test, including notification of events to the - * {@link RunNotifier} and {@link TestContextManager} as well as proper - * handling of {@link org.junit.Ignore @Ignore}, - * {@link org.junit.Test#expected() expected exceptions}, - * {@link org.junit.Test#timeout() test timeouts}, and - * {@link org.junit.Assume.AssumptionViolatedException assumptions}. - */ - public void run() { - if (this.testMethod.isIgnored()) { - this.notifier.fireTestIgnored(this.description); - return; - } - - this.notifier.fireTestStarted(this.description); - try { - Timed timedAnnotation = this.testMethod.getMethod().getAnnotation(Timed.class); - long springTimeout = (timedAnnotation != null && timedAnnotation.millis() > 0 ? - timedAnnotation.millis() : 0); - long junitTimeout = this.testMethod.getTimeout(); - if (springTimeout > 0 && junitTimeout > 0) { - throw new IllegalStateException("Test method [" + this.testMethod.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."); - } - else if (springTimeout > 0) { - long startTime = System.currentTimeMillis(); - try { - runTest(); - } - finally { - long elapsed = System.currentTimeMillis() - startTime; - if (elapsed > springTimeout) { - addFailure(new TimeoutException("Took " + elapsed + " ms; limit was " + springTimeout)); - } - } - } - else if (junitTimeout > 0) { - runWithTimeout(junitTimeout); - } - else { - runTest(); - } - } - finally { - this.notifier.fireTestFinished(this.description); - } - } - - /** - * Runs the test method on the test instance with the specified - * timeout. - * @param timeout the timeout in milliseconds - * @see #runWithRepetitions(Runnable) - * @see #runTestMethod() - */ - protected void runWithTimeout(final long timeout) throws CancellationException { - runWithRepetitions(new Runnable() { - public void run() { - ExecutorService service = Executors.newSingleThreadExecutor(); - Future result = service.submit(new RunBeforesThenTestThenAfters()); - service.shutdown(); - try { - boolean terminated = service.awaitTermination(timeout, TimeUnit.MILLISECONDS); - if (!terminated) { - service.shutdownNow(); - } - // Throws the exception if one occurred during the invocation. - result.get(0, TimeUnit.MILLISECONDS); - } - catch (TimeoutException ex) { - String message = "Test timed out after " + timeout + " milliseconds"; - addFailure(new TimeoutException(message)); - // We're cancelling repetitions here since we don't want - // the abandoned test method execution to conflict with - // further execution attempts of the same test method. - throw new CancellationException(message); - } - catch (ExecutionException ex) { - addFailure(ex.getCause()); - } - catch (Exception ex) { - addFailure(ex); - } - } - }); - } - - /** - * Runs the test, including {@link #runBefores() @Before} and - * {@link #runAfters() @After} methods. - * @see #runWithRepetitions(Runnable) - * @see #runTestMethod() - */ - protected void runTest() { - runWithRepetitions(new RunBeforesThenTestThenAfters()); - } - - /** - * Runs the supplied test with repetitions. Checks for the - * presence of {@link Repeat @Repeat} to determine if the test should be run - * more than once. The test will be run at least once. - * @param test the runnable test - * @see Repeat - */ - protected void runWithRepetitions(Runnable test) { - Method method = this.testMethod.getMethod(); - Repeat repeat = method.getAnnotation(Repeat.class); - int runs = (repeat != null && repeat.value() > 1 ? repeat.value() : 1); - - for (int i = 0; i < runs; i++) { - if (runs > 1 && logger.isInfoEnabled()) { - logger.info("Repetition " + (i + 1) + " of test " + method.getName()); - } - try { - test.run(); - } - catch (CancellationException ex) { - break; - } - } - } - - /** - * Runs the test method on the test instance, processing exceptions - * (both expected and unexpected), assumptions, and registering - * failures as necessary. - */ - protected void runTestMethod() { - this.testException = null; - try { - this.testMethod.invoke(this.testInstance); - if (this.testMethod.expectsException()) { - addFailure(new AssertionError("Expected exception: " + this.testMethod.getExpectedException().getName())); - } - } - catch (InvocationTargetException ex) { - this.testException = ex.getTargetException(); - if (!(this.testException instanceof AssumptionViolatedException)) { - if (!this.testMethod.expectsException()) { - addFailure(this.testException); - } - else if (this.testMethod.isUnexpected(this.testException)) { - addFailure(new Exception("Unexpected exception, expected <" + - this.testMethod.getExpectedException().getName() + "> but was <" + - this.testException.getClass().getName() + ">", this.testException)); - } - } - } - catch (Throwable ex) { - addFailure(ex); - } - finally { - if (logger.isDebugEnabled()) { - logger.debug("Test method [" + this.testMethod.getMethod() + "] threw exception: " + - this.testException); - } - } - } - - /** - * Calls {@link TestContextManager#beforeTestMethod} and then runs - * {@link org.junit.Before @Before methods}, registering failures - * and throwing {@link FailedBefore} exceptions as necessary. - * @throws FailedBefore if an error occurs while executing a before method - */ - protected void runBefores() throws FailedBefore { - try { - this.testContextManager.beforeTestMethod(this.testInstance, this.testMethod.getMethod()); - List befores = this.testMethod.getBefores(); - for (Method before : befores) { - before.invoke(this.testInstance); - } - } - catch (InvocationTargetException ex) { - Throwable targetEx = ex.getTargetException(); - if (!(targetEx instanceof AssumptionViolatedException)) { - addFailure(targetEx); - } - throw new FailedBefore(); - } - catch (Throwable ex) { - addFailure(ex); - throw new FailedBefore(); - } - } - - /** - * Runs {@link org.junit.After @After methods}, registering failures as - * necessary, and then calls {@link TestContextManager#afterTestMethod}. - */ - protected void runAfters() { - List afters = this.testMethod.getAfters(); - for (Method after : afters) { - try { - after.invoke(this.testInstance); - } - catch (InvocationTargetException ex) { - addFailure(ex.getTargetException()); - } - catch (Throwable ex) { - addFailure(ex); - } - } - try { - this.testContextManager.afterTestMethod(this.testInstance, this.testMethod.getMethod(), this.testException); - } - catch (Throwable ex) { - addFailure(ex); - } - } - - /** - * Fire a failure for the supplied exception with the - * {@link RunNotifier}. - * @param exception the exception upon which to base the failure - */ - protected void addFailure(Throwable exception) { - this.notifier.fireTestFailure(new Failure(this.description, exception)); - } - - - /** - * Runs the test method, executing @Before and @After - * methods accordingly. - */ - private class RunBeforesThenTestThenAfters implements Runnable { - - public void run() { - try { - runBefores(); - runTestMethod(); - } - catch (FailedBefore ex) { - // ignore - } - finally { - runAfters(); - } - } - } - - - /** - * Marker exception to signal that an exception was encountered while - * executing an {@link org.junit.Before @Before} method. - */ - private static class FailedBefore extends Exception { - - } - -} diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringTestMethod.java b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringTestMethod.java deleted file mode 100644 index 12f58277978..00000000000 --- a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/SpringTestMethod.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2002-2008 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.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.Test.None; -import org.junit.internal.runners.TestClass; - -import org.springframework.test.annotation.ExpectedException; -import org.springframework.test.annotation.ProfileValueSource; -import org.springframework.test.annotation.ProfileValueUtils; - -/** - * SpringTestMethod is a custom implementation of JUnit 4.4's - * {@link org.junit.internal.runners.TestMethod TestMethod}. Due to method and - * field visibility constraints, the code of TestMethod has been duplicated here - * instead of subclassing TestMethod directly. - * - *

SpringTestMethod also provides support for - * {@link org.springframework.test.annotation.IfProfileValue @IfProfileValue} - * and {@link ExpectedException @ExpectedException}. See {@link #isIgnored()} - * and {@link #getExpectedException()} for further details. - * - * @author Sam Brannen - * @since 2.5 - */ -class SpringTestMethod { - - private static final Log logger = LogFactory.getLog(SpringTestMethod.class); - - private final Method method; - - private final TestClass testClass; - - - /** - * Constructs a test method for the supplied {@link Method method} and - * {@link TestClass test class}; and retrieves the configured (or default) - * {@link ProfileValueSource}. - * @param method The test method - * @param testClass the test class - */ - public SpringTestMethod(Method method, TestClass testClass) { - this.method = method; - this.testClass = testClass; - } - - - /** - * Determine if this test method is {@link Test#expected() expected} to - * throw an exception. - */ - public boolean expectsException() { - return (getExpectedException() != null); - } - - /** - * Get the {@link After @After} methods for this test method. - */ - public List getAfters() { - return getTestClass().getAnnotatedMethods(After.class); - } - - /** - * Get the {@link Before @Before} methods for this test method. - */ - public List getBefores() { - return getTestClass().getAnnotatedMethods(Before.class); - } - - /** - * Get the exception that this 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 null if none was specified - */ - public Class getExpectedException() throws IllegalStateException { - ExpectedException expectedExAnn = getMethod().getAnnotation(ExpectedException.class); - Test testAnnotation = getMethod().getAnnotation(Test.class); - - Class expectedException = null; - Class springExpectedException = - (expectedExAnn != null && expectedExAnn.value() != null ? expectedExAnn.value() : null); - Class junitExpectedException = - (testAnnotation != null && testAnnotation.expected() != None.class ? testAnnotation.expected() : null); - - if (springExpectedException != null && junitExpectedException != null) { - String msg = "Test method [" + 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); - } - else if (springExpectedException != null) { - expectedException = springExpectedException; - } - else if (junitExpectedException != null) { - expectedException = junitExpectedException; - } - - return expectedException; - } - - /** - * Get the actual {@link Method method} referenced by this test method. - */ - public final Method getMethod() { - return this.method; - } - - /** - * Get the {@link TestClass test class} for this test method. - */ - public final TestClass getTestClass() { - return this.testClass; - } - - /** - * Get the configured timeout for this test method. - *

Supports JUnit's {@link Test#timeout() @Test(timeout=...)} annotation. - * @return the timeout, or 0 if none was specified - */ - public long getTimeout() { - Test testAnnotation = getMethod().getAnnotation(Test.class); - return (testAnnotation != null && testAnnotation.timeout() > 0 ? testAnnotation.timeout() : 0); - } - - /** - * Convenience method for {@link Method#invoke(Object,Object...) invoking} - * the method associated with this test method. Throws exceptions consistent - * with {@link Method#invoke(Object,Object...) Method.invoke()}. - * @param testInstance the test instance upon which to invoke the method - */ - public void invoke(Object testInstance) throws IllegalAccessException, InvocationTargetException { - getMethod().invoke(testInstance); - } - - /** - * Determine if this test method should be ignored. - * @return true if this test method should be ignored - * @see ProfileValueUtils#isTestEnabledInThisEnvironment - */ - public boolean isIgnored() { - return (getMethod().isAnnotationPresent(Ignore.class) || - !ProfileValueUtils.isTestEnabledInThisEnvironment(this.method, this.testClass.getJavaClass())); - } - - /** - * Determine if this test method {@link Test#expected() expects} exceptions - * of the type of the supplied exception to be thrown. - * @param exception the thrown exception - * @return true if the supplied exception was of an expected type - */ - public boolean isUnexpected(Throwable exception) { - return !getExpectedException().isAssignableFrom(exception.getClass()); - } - -} diff --git a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/package.html b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/package.html index 3b0b16fb86f..4eb73fa79e5 100644 --- a/org.springframework.test/src/main/java/org/springframework/test/context/junit4/package.html +++ b/org.springframework.test/src/main/java/org/springframework/test/context/junit4/package.html @@ -2,7 +2,7 @@

Support classes for ApplicationContext-based and transactional -tests run with JUnit 4.4 and the Spring TestContext Framework.

+tests run with JUnit 4.5 and the Spring TestContext Framework.