-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
@BeforeParam/@AfterParam for Parameterized runner #1435
Merged
+442
−28
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
066e1d6
@BeforeParam/@AfterParam for Parameterized runner
panchenko 45064f7
Fix minor style issues
kcooney 3390335
test for multiple @BeforeParam/@AfterParam
panchenko 1de2311
test for @BeforeParam/@AfterParam with multiple parameters
panchenko 15ac6ad
Validate number of parameters in @BeforeParam/@AfterParam
panchenko 70245b3
code style
panchenko e270f2c
iterate parameters once
panchenko 570ec30
style changes
panchenko 754732b
Style fixes
kcooney 35bbd8f
Make RunnersFactory.parameterCount final
kcooney 301a6e0
Add test cases for @Parameters methods returning Collection
kcooney 98e061a
reuse RunBefores/RunAfters
panchenko f406970
style
panchenko f7e3ad3
javadoc for @BeforeParam/@AfterParam
panchenko 76c1779
update javadoc
panchenko 03e598f
update header in javadoc
panchenko 4958ea8
javadoc: "parameters"
panchenko ab15e7e
since 4.13
panchenko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
package org.junit.runners; | ||
|
||
import java.lang.annotation.Annotation; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Inherited; | ||
import java.lang.annotation.Retention; | ||
|
@@ -8,11 +9,13 @@ | |
import java.text.MessageFormat; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import org.junit.runner.Runner; | ||
import org.junit.runners.model.FrameworkMethod; | ||
import org.junit.runners.model.InvalidTestClassError; | ||
import org.junit.runners.model.TestClass; | ||
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory; | ||
import org.junit.runners.parameterized.ParametersRunnerFactory; | ||
|
@@ -134,6 +137,19 @@ | |
* } | ||
* </pre> | ||
* | ||
* <h3>Executing code before/after executing tests for specific parameters</h3> | ||
* <p> | ||
* If your test needs to perform some preparation or cleanup based on the | ||
* parameters, this can be done by adding public static methods annotated with | ||
* {@code @BeforeParam}/{@code @AfterParam}. Such methods should either have no | ||
* parameters or the same parameters as the test. | ||
* <pre> | ||
* @BeforeParam | ||
* public static void beforeTestsForParameter(String onlyParameter) { | ||
* System.out.println("Testing " + onlyParameter); | ||
* } | ||
* </pre> | ||
* | ||
* <h3>Create different runners</h3> | ||
* <p> | ||
* By default the {@code Parameterized} runner creates a slightly modified | ||
|
@@ -234,32 +250,91 @@ public class Parameterized extends Suite { | |
Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class; | ||
} | ||
|
||
/** | ||
* Annotation for {@code public static void} methods which should be executed before | ||
* evaluating tests with particular parameters. | ||
* | ||
* @see org.junit.BeforeClass | ||
* @see org.junit.Before | ||
* @since 4.13 | ||
*/ | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.METHOD) | ||
public @interface BeforeParam { | ||
} | ||
|
||
/** | ||
* Annotation for {@code public static void} methods which should be executed after | ||
* evaluating tests with particular parameters. | ||
* | ||
* @see org.junit.AfterClass | ||
* @see org.junit.After | ||
* @since 4.13 | ||
*/ | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.METHOD) | ||
public @interface AfterParam { | ||
} | ||
|
||
/** | ||
* Only called reflectively. Do not use programmatically. | ||
*/ | ||
public Parameterized(Class<?> klass) throws Throwable { | ||
super(klass, RunnersFactory.createRunnersForClass(klass)); | ||
this(klass, new RunnersFactory(klass)); | ||
} | ||
|
||
private Parameterized(Class<?> klass, RunnersFactory runnersFactory) throws Exception { | ||
super(klass, runnersFactory.createRunners()); | ||
validateBeforeParamAndAfterParamMethods(runnersFactory.parameterCount); | ||
} | ||
|
||
private void validateBeforeParamAndAfterParamMethods(Integer parameterCount) | ||
throws InvalidTestClassError { | ||
List<Throwable> errors = new ArrayList<Throwable>(); | ||
validatePublicStaticVoidMethods(Parameterized.BeforeParam.class, parameterCount, errors); | ||
validatePublicStaticVoidMethods(Parameterized.AfterParam.class, parameterCount, errors); | ||
if (!errors.isEmpty()) { | ||
throw new InvalidTestClassError(getTestClass().getJavaClass(), errors); | ||
} | ||
} | ||
|
||
private void validatePublicStaticVoidMethods( | ||
Class<? extends Annotation> annotation, Integer parameterCount, | ||
List<Throwable> errors) { | ||
List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation); | ||
for (FrameworkMethod fm : methods) { | ||
fm.validatePublicVoid(true, errors); | ||
if (parameterCount != null) { | ||
int methodParameterCount = fm.getMethod().getParameterTypes().length; | ||
if (methodParameterCount != 0 && methodParameterCount != parameterCount) { | ||
errors.add(new Exception("Method " + fm.getName() | ||
+ "() should have 0 or " + parameterCount + " parameter(s)")); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private static class RunnersFactory { | ||
private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory(); | ||
|
||
private final TestClass testClass; | ||
private final FrameworkMethod parametersMethod; | ||
private final List<Object> allParameters; | ||
private final int parameterCount; | ||
|
||
static List<Runner> createRunnersForClass(Class<?> klass) | ||
throws Throwable { | ||
return new RunnersFactory(klass).createRunners(); | ||
} | ||
|
||
private RunnersFactory(Class<?> klass) { | ||
private RunnersFactory(Class<?> klass) throws Throwable { | ||
testClass = new TestClass(klass); | ||
parametersMethod = getParametersMethod(testClass); | ||
allParameters = allParameters(testClass, parametersMethod); | ||
parameterCount = | ||
allParameters.isEmpty() ? 0 : normalizeParameters(allParameters.get(0)).length; | ||
} | ||
|
||
private List<Runner> createRunners() throws Throwable { | ||
Parameters parameters = getParametersMethod().getAnnotation( | ||
Parameters.class); | ||
private List<Runner> createRunners() throws Exception { | ||
Parameters parameters = parametersMethod.getAnnotation(Parameters.class); | ||
return Collections.unmodifiableList(createRunnersForParameters( | ||
allParameters(), parameters.name(), | ||
allParameters, parameters.name(), | ||
getParametersRunnerFactory())); | ||
} | ||
|
||
|
@@ -278,25 +353,37 @@ private ParametersRunnerFactory getParametersRunnerFactory() | |
|
||
private TestWithParameters createTestWithNotNormalizedParameters( | ||
String pattern, int index, Object parametersOrSingleParameter) { | ||
Object[] parameters = (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter | ||
Object[] parameters = normalizeParameters(parametersOrSingleParameter); | ||
return createTestWithParameters(testClass, pattern, index, parameters); | ||
} | ||
|
||
private static Object[] normalizeParameters(Object parametersOrSingleParameter) { | ||
return (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter | ||
: new Object[] { parametersOrSingleParameter }; | ||
return createTestWithParameters(testClass, pattern, index, | ||
parameters); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private Iterable<Object> allParameters() throws Throwable { | ||
Object parameters = getParametersMethod().invokeExplosively(null); | ||
if (parameters instanceof Iterable) { | ||
return (Iterable<Object>) parameters; | ||
private static List<Object> allParameters( | ||
TestClass testClass, FrameworkMethod parametersMethod) throws Throwable { | ||
Object parameters = parametersMethod.invokeExplosively(null); | ||
if (parameters instanceof List) { | ||
return (List<Object>) parameters; | ||
} else if (parameters instanceof Collection) { | ||
return new ArrayList<Object>((Collection<Object>) parameters); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kcooney IMHO there is no need to handle |
||
} else if (parameters instanceof Iterable) { | ||
List<Object> result = new ArrayList<Object>(); | ||
for (Object entry : ((Iterable<Object>) parameters)) { | ||
result.add(entry); | ||
} | ||
return result; | ||
} else if (parameters instanceof Object[]) { | ||
return Arrays.asList((Object[]) parameters); | ||
} else { | ||
throw parametersMethodReturnedWrongType(); | ||
throw parametersMethodReturnedWrongType(testClass, parametersMethod); | ||
} | ||
} | ||
|
||
private FrameworkMethod getParametersMethod() throws Exception { | ||
private static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { | ||
List<FrameworkMethod> methods = testClass | ||
.getAnnotatedMethods(Parameters.class); | ||
for (FrameworkMethod each : methods) { | ||
|
@@ -322,7 +409,7 @@ private List<Runner> createRunnersForParameters( | |
} | ||
return runners; | ||
} catch (ClassCastException e) { | ||
throw parametersMethodReturnedWrongType(); | ||
throw parametersMethodReturnedWrongType(testClass, parametersMethod); | ||
} | ||
} | ||
|
||
|
@@ -338,9 +425,10 @@ private List<TestWithParameters> createTestsForParameters( | |
return children; | ||
} | ||
|
||
private Exception parametersMethodReturnedWrongType() throws Exception { | ||
private static Exception parametersMethodReturnedWrongType( | ||
TestClass testClass, FrameworkMethod parametersMethod) throws Exception { | ||
String className = testClass.getName(); | ||
String methodName = getParametersMethod().getName(); | ||
String methodName = parametersMethod.getName(); | ||
String message = MessageFormat.format( | ||
"{0}.{1}() must return an Iterable of arrays.", className, | ||
methodName); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we delete
validateBeforeParamAndAfterParamMethods()
and instead overridecollectInitializationErrors(List<Throwable>)
and callvalidatePublicStaticVoidMethods
there?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that before,
collectInitializationErrors()
is called from the super class constructor and the number of method parameters can't be easily validated (only with some tricks, e.g. usingThreadLocal
).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see. I've never been a fan of calling non-static methods in constructors. It's caused no end of problems in JUnit4. Hopefully they avoided that in the JUnit5 code base