/*
* 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 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.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.springframework.test.annotation.ProfileValueUtils;
import org.springframework.test.context.TestContextManager;
/**
* <p>
* SpringJUnit4ClassRunner is a custom extension of {@link JUnit4ClassRunner}
* which provides functionality of the <em>Spring TestContext Framework</em>
* to standard JUnit 4.4+ tests by means of the {@link TestContextManager} and
* associated support classes and annotations.
* </p>
* <p>
* The following list constitutes all annotations currently supported directly
* by SpringJUnit4ClassRunner.
* <em>(Note that additional annotations may be supported by various
* {@link org.springframework.test.context.TestExecutionListener TestExecutionListeners})</em>
* </p>
* <ul>
* <li>{@link org.junit.Test#expected() @Test(expected=...)}</li>
* <li>{@link org.springframework.test.annotation.ExpectedException @ExpectedException}</li>
* <li>{@link org.junit.Test#timeout() @Test(timeout=...)}</li>
* <li>{@link org.springframework.test.annotation.Timed @Timed}</li>
* <li>{@link org.springframework.test.annotation.Repeat @Repeat}</li>
* <li>{@link org.junit.Ignore @Ignore}</li>
* <li>{@link org.springframework.test.annotation.ProfileValueSourceConfiguration @ProfileValueSourceConfiguration}</li>
* <li>{@link org.springframework.test.annotation.IfProfileValue @IfProfileValue}</li>
* </ul>
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
* @see TestContextManager
*/
public class SpringJUnit4ClassRunner extends JUnit4ClassRunner {
private static final Log logger = LogFactory.getLog(SpringJUnit4ClassRunner.class);
private final TestContextManager testContextManager;
/**
* Constructs a new <code>SpringJUnit4ClassRunner</code> and initializes a
* {@link TestContextManager} to provide Spring testing functionality to
* standard JUnit tests.
* @param clazz the Class object corresponding to 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 + "].");
}
this.testContextManager = createTestContextManager(clazz);
}
@Override
/**
* Check whether the test is enabled in the first place. This prevents classes with
* a non-matching <code>@IfProfileValue</code> annotation from running altogether,
* even skipping the execution of <code>prepareTestInstance</code> listener methods.
* @see org.springframework.test.annotation.IfProfileValue
* @see org.springframework.test.context.TestExecutionListener
*/
public void run(RunNotifier notifier) {
if (!ProfileValueUtils.isTestEnabledInThisEnvironment(getTestClass().getJavaClass())) {
notifier.fireTestIgnored(getDescription());
return;
}
super.run(notifier);
}
/**
* 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.
* @see JUnit4ClassRunner#createTest()
* @see TestContextManager#prepareTestInstance(Object)
*/
@Override
protected Object createTest() throws Exception {
Object testInstance = super.createTest();
getTestContextManager().prepareTestInstance(testInstance);
return testInstance;
}
/**
* 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;
}
/**
* Invokes the supplied {@link Method test method} and notifies the supplied
* {@link RunNotifier} of the appropriate events.
* @see #createTest()
* @see JUnit4ClassRunner#invokeTestMethod(Method,RunNotifier)
*/
@Override
protected void invokeTestMethod(Method method, RunNotifier notifier) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking test method [" + method.toGenericString() + "]");
}
// 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.
Description description = methodDescription(method);
Object testInstance;
try {
testInstance = createTest();
}
catch (InvocationTargetException ex) {
notifier.testAborted(description, ex.getCause());
return;
}
catch (Exception ex) {
notifier.testAborted(description, ex);
return;
}
SpringTestMethod testMethod = new SpringTestMethod(method, getTestClass());
new SpringMethodRoadie(getTestContextManager(), testInstance, testMethod, notifier, description).run();
}
}