Skip to content

Commit cb6abf6

Browse files
committed
Use a ParametersRunnerFactory for creating the test for a set of parameters.
There's now an explicit component for creating the runner of a single set of parameters. IMHO this is a better extension point than overriding a method. The factory can be specified by the @UseParametersRunnerFactory annotation or you can subclass the Parameterized class and provide the factory to its constructor.
1 parent dca4a94 commit cb6abf6

File tree

4 files changed

+179
-26
lines changed

4 files changed

+179
-26
lines changed

src/main/java/org/junit/runners/Parameterized.java

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
import org.junit.runners.model.FrameworkMethod;
1515
import org.junit.runners.model.InitializationError;
1616
import org.junit.runners.model.TestClass;
17-
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
17+
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory;
18+
import org.junit.runners.parameterized.ParametersRunnerFactory;
1819
import org.junit.runners.parameterized.TestWithParameters;
1920

2021
/**
@@ -128,6 +129,36 @@
128129
* }
129130
* </pre>
130131
*
132+
* <h3>Create different runners</h3>
133+
* <p>
134+
* By default the {@code Parameterized} runner creates a slightly modified
135+
* {@link BlockJUnit4ClassRunner} for each set of parameters. You can build an
136+
* own {@code Parameterized} runner that creates another runner for each set of
137+
* parameters. Therefore you have to build a {@link ParametersRunnerFactory}
138+
* that creates a runner for each {@link TestWithParameters}. (
139+
* {@code TestWithParameters} are bundling the parameters and the test name.)
140+
* The factory must have a public zero-arg constructor.
141+
*
142+
* <pre>
143+
* public class YourRunnerFactory implements ParameterizedRunnerFactory {
144+
* public Runner createRunnerForTestWithParameters(TestWithParameters test)
145+
* throws InitializationError {
146+
* return YourRunner(test);
147+
* }
148+
* }
149+
* </pre>
150+
* <p>
151+
* Use the {@link UseParametersRunnerFactory} to tell the {@code Parameterized}
152+
* runner that it should use your factory.
153+
*
154+
* <pre>
155+
* &#064;RunWith(Parameterized.class)
156+
* &#064;UseParametersRunnerFactory(YourRunnerFactory.class)
157+
* public class YourTest {
158+
* ...
159+
* }
160+
* </pre>
161+
*
131162
* @since 4.0
132163
*/
133164
public class Parameterized extends Suite {
@@ -181,6 +212,24 @@ public class Parameterized extends Suite {
181212
int value() default 0;
182213
}
183214

215+
/**
216+
* Add this annotation to your test class if you want to generate a special
217+
* runner. You have to specify a {@link ParametersRunnerFactory} class that
218+
* creates such runners. The factory must have a public zero-arg
219+
* constructor.
220+
*/
221+
@Retention(RetentionPolicy.RUNTIME)
222+
@Target(ElementType.TYPE)
223+
public @interface UseParametersRunnerFactory {
224+
/**
225+
* @return a {@link ParametersRunnerFactory} class (must have a default
226+
* constructor)
227+
*/
228+
Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class;
229+
}
230+
231+
private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();
232+
184233
private static final List<Runner> NO_RUNNERS = Collections.<Runner>emptyList();
185234

186235
private final List<Runner> fRunners;
@@ -190,29 +239,38 @@ public class Parameterized extends Suite {
190239
*/
191240
public Parameterized(Class<?> klass) throws Throwable {
192241
super(klass, NO_RUNNERS);
242+
ParametersRunnerFactory runnerFactory = getParametersRunnerFactory(
243+
klass);
193244
Parameters parameters = getParametersMethod().getAnnotation(
194245
Parameters.class);
195-
fRunners = Collections.unmodifiableList(createRunnersForParameters(allParameters(), parameters.name()));
246+
fRunners = Collections.unmodifiableList(createRunnersForParameters(
247+
allParameters(), parameters.name(), runnerFactory));
248+
}
249+
250+
private ParametersRunnerFactory getParametersRunnerFactory(Class<?> klass)
251+
throws InstantiationException, IllegalAccessException {
252+
UseParametersRunnerFactory annotation = klass
253+
.getAnnotation(UseParametersRunnerFactory.class);
254+
if (annotation == null) {
255+
return DEFAULT_FACTORY;
256+
} else {
257+
Class<? extends ParametersRunnerFactory> factoryClass = annotation
258+
.value();
259+
return factoryClass.newInstance();
260+
}
196261
}
197262

198263
@Override
199264
protected List<Runner> getChildren() {
200265
return fRunners;
201266
}
202267

203-
private Runner createRunnerWithNotNormalizedParameters(String pattern,
204-
int index, Object parametersOrSingleParameter)
205-
throws InitializationError {
268+
private TestWithParameters createTestWithNotNormalizedParameters(
269+
String pattern, int index, Object parametersOrSingleParameter) {
206270
Object[] parameters= (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter
207271
: new Object[] { parametersOrSingleParameter };
208-
TestWithParameters test = createTestWithParameters(getTestClass(),
209-
pattern, index, parameters);
210-
return createRunnerForTest(test);
211-
}
212-
213-
protected Runner createRunnerForTest(TestWithParameters test)
214-
throws InitializationError {
215-
return new BlockJUnit4ClassRunnerWithParameters(test);
272+
return createTestWithParameters(getTestClass(), pattern, index,
273+
parameters);
216274
}
217275

218276
@SuppressWarnings("unchecked")
@@ -240,20 +298,37 @@ private FrameworkMethod getParametersMethod() throws Exception {
240298
+ getTestClass().getName());
241299
}
242300

243-
private List<Runner> createRunnersForParameters(Iterable<Object> allParameters, String namePattern) throws Exception {
301+
private List<Runner> createRunnersForParameters(
302+
Iterable<Object> allParameters, String namePattern,
303+
ParametersRunnerFactory runnerFactory)
304+
throws InitializationError,
305+
Exception {
244306
try {
245-
int i = 0;
246-
List<Runner> children = new ArrayList<Runner>();
247-
for (Object parametersOfSingleTest : allParameters) {
248-
children.add(createRunnerWithNotNormalizedParameters(
249-
namePattern, i++, parametersOfSingleTest));
307+
List<TestWithParameters> tests = createTestsForParameters(
308+
allParameters, namePattern);
309+
List<Runner> runners = new ArrayList<Runner>();
310+
for (TestWithParameters test : tests) {
311+
runners.add(runnerFactory
312+
.createRunnerForTestWithParameters(test));
250313
}
251-
return children;
314+
return runners;
252315
} catch (ClassCastException e) {
253316
throw parametersMethodReturnedWrongType();
254317
}
255318
}
256319

320+
private List<TestWithParameters> createTestsForParameters(
321+
Iterable<Object> allParameters, String namePattern)
322+
throws Exception {
323+
int i = 0;
324+
List<TestWithParameters> children = new ArrayList<TestWithParameters>();
325+
for (Object parametersOfSingleTest : allParameters) {
326+
children.add(createTestWithNotNormalizedParameters(namePattern,
327+
i++, parametersOfSingleTest));
328+
}
329+
return children;
330+
}
331+
257332
private Exception parametersMethodReturnedWrongType() throws Exception {
258333
String className = getTestClass().getName();
259334
String methodName = getParametersMethod().getName();
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.junit.runners.parameterized;
2+
3+
import org.junit.runner.Runner;
4+
import org.junit.runners.model.InitializationError;
5+
6+
/**
7+
* A {@link ParametersRunnerFactory} that creates
8+
* {@link BlockJUnit4ClassRunnerWithParameters}.
9+
*
10+
* @since 4.12
11+
*/
12+
public class BlockJUnit4ClassRunnerWithParametersFactory implements
13+
ParametersRunnerFactory {
14+
public Runner createRunnerForTestWithParameters(TestWithParameters test)
15+
throws InitializationError {
16+
return new BlockJUnit4ClassRunnerWithParameters(test);
17+
}
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package org.junit.runners.parameterized;
2+
3+
import org.junit.runner.Runner;
4+
import org.junit.runners.model.InitializationError;
5+
6+
/**
7+
* A {@code ParameterizedRunnerFactory} creates a runner for a single
8+
* {@link TestWithParameters}.
9+
*
10+
* @since 4.12
11+
*/
12+
public interface ParametersRunnerFactory {
13+
/**
14+
* Returns a runner for the specified {@link TestWithParameters}.
15+
*
16+
* @throws InitializationError
17+
* if the runner could not be created.
18+
*/
19+
Runner createRunnerForTestWithParameters(TestWithParameters test)
20+
throws InitializationError;
21+
}

src/test/java/org/junit/tests/running/classes/ParameterizedTestTest.java

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
import org.junit.runners.Parameterized;
2626
import org.junit.runners.Parameterized.Parameter;
2727
import org.junit.runners.Parameterized.Parameters;
28+
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
2829
import org.junit.runners.model.InitializationError;
30+
import org.junit.runners.parameterized.ParametersRunnerFactory;
31+
import org.junit.runners.parameterized.TestWithParameters;
2932

3033
public class ParameterizedTestTest {
3134
@RunWith(Parameterized.class)
@@ -307,12 +310,10 @@ public void aTest() {
307310
}
308311

309312
@Test
310-
public void meaningfulFailureWhenParametersNotPublic() throws Exception {
311-
Result result = JUnitCore.runClasses(ProtectedParametersTest.class);
312-
String expected = String.format(
313-
"No public static parameters method on class %s",
314-
ProtectedParametersTest.class.getName());
315-
assertEquals(expected, result.getFailures().get(0).getMessage());
313+
public void meaningfulFailureWhenParametersNotPublic() {
314+
assertTestCreatesSingleFailureWithMessage(ProtectedParametersTest.class,
315+
"No public static parameters method on class "
316+
+ ProtectedParametersTest.class.getName());
316317
}
317318

318319
@RunWith(Parameterized.class)
@@ -430,4 +431,42 @@ public void runsForEverySingleArgumentOfIterable() {
430431
.runClasses(SingleArgumentTestWithIterable.class);
431432
assertEquals(2, result.getRunCount());
432433
}
434+
435+
static public class ExceptionThrowingRunnerFactory implements
436+
ParametersRunnerFactory {
437+
public Runner createRunnerForTestWithParameters(TestWithParameters test)
438+
throws InitializationError {
439+
throw new InitializationError(
440+
"Called ExceptionThrowingRunnerFactory.");
441+
}
442+
}
443+
444+
@RunWith(Parameterized.class)
445+
@UseParametersRunnerFactory(ExceptionThrowingRunnerFactory.class)
446+
static public class TestWithUseParametersRunnerFactoryAnnotation {
447+
@Parameters
448+
public static Iterable<? extends Object> data() {
449+
return asList("single test");
450+
}
451+
452+
public TestWithUseParametersRunnerFactoryAnnotation(Object argument) {
453+
}
454+
455+
@Test
456+
public void aTest() {
457+
}
458+
}
459+
460+
@Test
461+
public void usesParametersRunnerFactoryThatWasSpecifiedByAnnotation() {
462+
assertTestCreatesSingleFailureWithMessage(
463+
TestWithUseParametersRunnerFactoryAnnotation.class,
464+
"Called ExceptionThrowingRunnerFactory.");
465+
}
466+
467+
private void assertTestCreatesSingleFailureWithMessage(Class<?> test, String message) {
468+
Result result = JUnitCore.runClasses(test);
469+
assertEquals(1, result.getFailures().size());
470+
assertEquals(message, result.getFailures().get(0).getMessage());
471+
}
433472
}

0 commit comments

Comments
 (0)