Skip to content

SPR-10217 Implement JUnit 4 Support using Rules #222

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright 2004-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 static org.junit.Assume.assumeTrue;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.springframework.test.annotation.ProfileValueUtils;
import org.springframework.test.context.TestContextManager;


/**
* <p>
* {@code SpringJUnitClassRule} is a custom {@link TestRule} which provides
* functionality of the <em>Spring TestContext Framework</em> to standard
* JUnit 4.9+ tests by means of the {@link TestContextManager} and associated
* support classes and annotations.
* </p>
* <p>
* Compared to {@link SpringJUnit4ClassRunner} the rule based JUnit support
* has the advantage that it is independent of the runner and can therefore
* be combined with existing 3rd party runners like {@code Parameterized}.
* </p>
* <p>
* However to achieve the same functionality as {@link SpringJUnit4ClassRunner}
* {@code SpringJUnitClassRule} has to be combined with
* {@link SpringJUnitMethodRule} as {@code SpringJUnitClassRule} only provides
* the class level features of {@link SpringJUnit4ClassRunner}.
* </p>
*
* <p>
* The following example shows you how to use {@code SpringJUnitClassRule}.
* </p>
* <pre><code>
* public class ExampleTest {
*
* @ClassRule
* public static final SpringJUnitClassRule CLASS_RULE = new SpringJUnitClassRule();
*
* @Rule
* public MethodRule methodRule = new SpringJUnitMethodRule(CLASS_RULE);
* }
* </code></pre>
*
* @author Philippe Marschall
* @since 3.2.2
* @see SpringJUnit4ClassRunner
* @see TestContextManager
* @see SpringJUnitMethodRule
*/
public class SpringJUnitClassRule implements TestRule {

// volatile since SpringJUnitMethodRule can potentially access it from a
// different thread depending on the runner.
private volatile TestContextManager testContextManager;

/**
* Get the {@link TestContextManager} associated with this runner.
* Will be {@code null} until this class is called by the JUnit framework.
*/
protected final TestContextManager getTestContextManager() {
return this.testContextManager;
}


/**
* Creates a new {@link TestContextManager} for the supplied test class and
* the configured <em>default {@code ContextLoader} class name</em>.
* 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 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 &#064;ContextConfiguration} annotation.
* <p>The default implementation returns {@code null}, thus implying use
* of the <em>standard</em> 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;
}


/**
* Check whether the test is enabled in the first place. This prevents
* classes with a non-matching {@code &#064;IfProfileValue} annotation
* from running altogether, even skipping the execution of
* {@code prepareTestInstance()} {@code TestExecutionListener}
* methods.
* Creates the {@link TestContextManager} and runs it's before and after
* class methods.
*
* @see ProfileValueUtils#isTestEnabledInThisEnvironment(Class)
* @see org.springframework.test.annotation.IfProfileValue
* @see org.springframework.test.context.TestExecutionListener
* @see #createTestContextManager(Class)
* @see TestContextManager#beforeTestClass()
* @see TestContextManager#afterTestClass()
*/
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {

@Override
public void evaluate() throws Throwable {
Class<?> testClass = description.getTestClass();
boolean profileIsActive = ProfileValueUtils.isTestEnabledInThisEnvironment(testClass);
assumeTrue("required profile not active", profileIsActive);
testContextManager = createTestContextManager(testClass);
testContextManager.beforeTestClass();
try {
base.evaluate();
} finally {
testContextManager.afterTestClass();
// make test context manager eligible for garbage collection
testContextManager = null;
}

}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright 2004-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 static org.junit.Assume.assumeTrue;

import java.lang.reflect.Method;

import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.springframework.test.annotation.ExpectedException;
import org.springframework.test.annotation.ProfileValueUtils;
import org.springframework.test.context.TestContextManager;


/**
* <p>
* {@code MethodRule} is a custom {@link MethodRule} which together with
* {@link SpringJUnitClassRule} provides functionality of the <em>Spring
* TestContext Framework</em> to standard JUnit 4.9+ tests by means of the
* {@link TestContextManager} and associated support classes and annotations.
* </p>
* <p>
* The only feature not supported of {@link SpringJUnit4ClassRunner} is
* {@link ExpectedException}.
* </p>
*
* @author Philippe Marschall
* @since 3.2.2
* @see SpringJUnitClassRule
*/
public class SpringJUnitMethodRule implements MethodRule {

// Hold on to the class rule instead of the TestContextManager because
// the class rule "owns" the TestContextManager can releases it when no
// longer needed.
private final SpringJUnitClassRule classRule;

/**
* Constructs a new {@code SpringJUnitMethodRule}.
*
* @param classRule the class rule, not {@code null},
* the class rule has to be defined in the same test class
* where this {@link SpringJUnitMethodRule} instance is defined
*/
public SpringJUnitMethodRule(SpringJUnitClassRule classRule) {
this.classRule = classRule;
}

/**
* Prepares the test instance (performs the injection) and runs the before
* and after test methods on the {@link TestContextManager}.
*
* @see TestContextManager#prepareTestInstance(Object)
* @see TestContextManager#beforeTestMethod(Object, Method)
* @see TestContextManager#afterTestMethod(Object, Method, Throwable)
*/
@Override
public Statement apply(final Statement base, final FrameworkMethod method, final Object target) {
return new Statement() {

@Override
public void evaluate() throws Throwable {
Method testMethod = method.getMethod();
boolean profileIsActive = ProfileValueUtils.isTestEnabledInThisEnvironment(testMethod, target.getClass());
assumeTrue("required profile not active", profileIsActive);
TestContextManager testContextManager = classRule.getTestContextManager();
testContextManager.prepareTestInstance(target);
testContextManager.beforeTestMethod(target, testMethod);
try {
base.evaluate();
}
catch (Throwable t) {
testContextManager.afterTestMethod(target, testMethod, t);
throw t;
}
testContextManager.afterTestMethod(target, testMethod, null);
}
};
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* <p>Support classes for ApplicationContext-based and transactional
* tests run with JUnit 4.5+ and the <em>Spring TestContext Framework</em>.</p>
* tests run with JUnit 4.5+ and the <em>Spring TestContext Framework</em>.
* The rules need JUnit 4.9+</p>
*/

package org.springframework.test.context.junit4;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2004-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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
import org.springframework.test.annotation.IfProfileValue;
import org.springframework.test.context.TestExecutionListeners;


/**
* Same as {@link EnabledAndIgnoredSpringRunnerTests} but for rule based tests.
*
* @author Philippe Marschall
* @since 3.2.2
* @see EnabledAndIgnoredSpringRunnerTests
*/
@TestExecutionListeners( {})
public class EnabledAndIgnoredSpringRuleTests {

@ClassRule
public static final SpringJUnitClassRule CLASS_RULE = new SpringJUnitClassRule();

@Rule
public MethodRule methodRule = new SpringJUnitMethodRule(CLASS_RULE);

protected static final String NAME = "EnabledAndIgnoredSpringRunnerTests.profile_value.name";

protected static final String VALUE = "enigma";

protected static int numTestsExecuted = 0;


@BeforeClass
public static void setProfileValue() {
numTestsExecuted = 0;
System.setProperty(NAME, VALUE);
}

@AfterClass
public static void verifyNumTestsExecuted() {
assertEquals("Verifying the number of tests executed.", 3, numTestsExecuted);
}

@Test
@IfProfileValue(name = NAME, value = VALUE + "X")
public void testIfProfileValueDisabled() {
numTestsExecuted++;
fail("The body of a disabled test should never be executed!");
}

@Test
@IfProfileValue(name = NAME, value = VALUE)
public void testIfProfileValueEnabledViaSingleValue() {
numTestsExecuted++;
}

@Test
@IfProfileValue(name = NAME, values = { "foo", VALUE, "bar" })
public void testIfProfileValueEnabledViaMultipleValues() {
numTestsExecuted++;
}

@Test
public void testIfProfileValueNotConfigured() {
numTestsExecuted++;
}

@Test
@Ignore
public void testJUnitIgnoreAnnotation() {
numTestsExecuted++;
fail("The body of an ignored test should never be executed!");
}

}
Loading