Skip to content

Add support for @Parameters in SpringJUnit4ClassRunner #277

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

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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.
* <p>
* Invoked internally by the {@link SpringJUnit4ClassRunner}.
*
* @since 3.2
* @author Gaetan Pitteloud
*/
class SpringJUnit4ParameterizedClassRunner extends Suite {

private final ArrayList<Runner> runners = new ArrayList<Runner>();

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<Throwable> errors) {
validateOnlyOneConstructor(errors);
}

@Override
protected Statement classBlock(RunNotifier notifier) {
return childrenInvoker(notifier);
}

}

public SpringJUnit4ParameterizedClassRunner(Class<?> klass, Method parametersMethod)
throws InitializationError {
super(klass, Collections.<Runner> 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<Runner> getChildren() {
return runners;
}

private Collection<?> getParametersList(Method parametersMethod) throws InitializationError {
try {
return (Collection<?>) parametersMethod.invoke(null);
} catch (Exception e) {
throw new InitializationError(e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<FrameworkMethod> computeTestMethods() {
List<FrameworkMethod> testMethods = super.computeTestMethods();

java.util.Collections.sort(testMethods, new Comparator<FrameworkMethod>() {

@Override
public int compare(FrameworkMethod method1, FrameworkMethod method2) {
return method1.getName().compareTo(method2.getName());
}
});

return testMethods;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<FrameworkMethod> computeTestMethods() {
List<FrameworkMethod> testMethods = super.computeTestMethods();

java.util.Collections.sort(testMethods, new Comparator<FrameworkMethod>() {

@Override
public int compare(FrameworkMethod method1, FrameworkMethod method2) {
return method1.getName().compareTo(method2.getName());
}
});

return testMethods;
}

}
Loading