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.
*
@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).
+ *
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 extends Throwable> 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, ornull if none was
+ * specified
*/
- @Override
- protected void invokeTestMethod(Method method, RunNotifier notifier) {
- if (logger.isDebugEnabled()) {
- logger.debug("Invoking test method [" + method.toGenericString() + "]");
+ protected Class extends Throwable> getExpectedException(FrameworkMethod frameworkMethod) {
+ Test testAnnotation = frameworkMethod.getAnnotation(Test.class);
+ Class extends Throwable> junitExpectedException = testAnnotation != null
+ && testAnnotation.expected() != Test.None.class ? testAnnotation.expected() : null;
+
+ ExpectedException expectedExAnn = frameworkMethod.getAnnotation(ExpectedException.class);
+ Class extends Throwable> 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.
- *
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());
- Listexception 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 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 Supports JUnit's {@link Test#timeout() @Test(timeout=...)} annotation.
- * @return the timeout, or Support classes for ApplicationContext-based and transactional
-tests run with JUnit 4.4 and the Spring TestContext Framework.exception that this test method is expected to throw.
- * null if none was specified
- */
- public Class extends Throwable> getExpectedException() throws IllegalStateException {
- ExpectedException expectedExAnn = getMethod().getAnnotation(ExpectedException.class);
- Test testAnnotation = getMethod().getAnnotation(Test.class);
-
- Class extends Throwable> expectedException = null;
- Class extends Throwable> springExpectedException =
- (expectedExAnn != null && expectedExAnn.value() != null ? expectedExAnn.value() : null);
- Class extends Throwable> 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.
- * 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 @@