1
1
package org .junit .runners ;
2
2
3
- import java .lang .annotation .Annotation ;
4
3
import java .lang .annotation .ElementType ;
5
4
import java .lang .annotation .Retention ;
6
5
import java .lang .annotation .RetentionPolicy ;
7
6
import java .lang .annotation .Target ;
8
- import java .lang .reflect .Field ;
9
7
import java .text .MessageFormat ;
10
8
import java .util .ArrayList ;
11
9
import java .util .Arrays ;
12
10
import java .util .Collections ;
13
11
import java .util .List ;
14
12
15
13
import org .junit .runner .Runner ;
16
- import org .junit .runner .notification .RunNotifier ;
17
- import org .junit .runners .model .FrameworkField ;
18
14
import org .junit .runners .model .FrameworkMethod ;
19
15
import org .junit .runners .model .InitializationError ;
20
- import org .junit .runners .model .Statement ;
16
+ import org .junit .runners .model .TestClass ;
17
+ import org .junit .runners .parameterized .BlockJUnit4ClassRunnerWithParametersFactory ;
18
+ import org .junit .runners .parameterized .ParametersRunnerFactory ;
19
+ import org .junit .runners .parameterized .TestWithParameters ;
21
20
22
21
/**
23
22
* The custom runner <code>Parameterized</code> implements parameterized tests.
130
129
* }
131
130
* </pre>
132
131
*
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
+ * @RunWith(Parameterized.class)
156
+ * @UseParametersRunnerFactory(YourRunnerFactory.class)
157
+ * public class YourTest {
158
+ * ...
159
+ * }
160
+ * </pre>
161
+ *
133
162
* @since 4.0
134
163
*/
135
164
public class Parameterized extends Suite {
@@ -183,118 +212,24 @@ public class Parameterized extends Suite {
183
212
int value () default 0 ;
184
213
}
185
214
186
- protected class TestClassRunnerForParameters extends BlockJUnit4ClassRunner {
187
- private final Object [] fParameters ;
188
-
189
- private final String fName ;
190
-
191
- protected TestClassRunnerForParameters (Class <?> type , String pattern , int index , Object [] parameters ) throws InitializationError {
192
- super (type );
193
-
194
- fParameters = parameters ;
195
- fName = nameFor (pattern , index , parameters );
196
- }
197
-
198
- @ Override
199
- public Object createTest () throws Exception {
200
- if (fieldsAreAnnotated ()) {
201
- return createTestUsingFieldInjection ();
202
- } else {
203
- return createTestUsingConstructorInjection ();
204
- }
205
- }
206
-
207
- private Object createTestUsingConstructorInjection () throws Exception {
208
- return getTestClass ().getOnlyConstructor ().newInstance (fParameters );
209
- }
210
-
211
- private Object createTestUsingFieldInjection () throws Exception {
212
- List <FrameworkField > annotatedFieldsByParameter = getAnnotatedFieldsByParameter ();
213
- if (annotatedFieldsByParameter .size () != fParameters .length ) {
214
- throw new Exception ("Wrong number of parameters and @Parameter fields." +
215
- " @Parameter fields counted: " + annotatedFieldsByParameter .size () + ", available parameters: " + fParameters .length + "." );
216
- }
217
- Object testClassInstance = getTestClass ().getJavaClass ().newInstance ();
218
- for (FrameworkField each : annotatedFieldsByParameter ) {
219
- Field field = each .getField ();
220
- Parameter annotation = field .getAnnotation (Parameter .class );
221
- int index = annotation .value ();
222
- try {
223
- field .set (testClassInstance , fParameters [index ]);
224
- } catch (IllegalArgumentException iare ) {
225
- throw new Exception (getTestClass ().getName () + ": Trying to set " + field .getName () +
226
- " with the value " + fParameters [index ] +
227
- " that is not the right type (" + fParameters [index ].getClass ().getSimpleName () + " instead of " +
228
- field .getType ().getSimpleName () + ")." , iare );
229
- }
230
- }
231
- return testClassInstance ;
232
- }
233
-
234
- protected String nameFor (String pattern , int index , Object [] parameters ) {
235
- String finalPattern = pattern .replaceAll ("\\ {index\\ }" , Integer .toString (index ));
236
- String name = MessageFormat .format (finalPattern , parameters );
237
- return "[" + name + "]" ;
238
- }
239
-
240
- @ Override
241
- protected String getName () {
242
- return fName ;
243
- }
244
-
245
- @ Override
246
- protected String testName (FrameworkMethod method ) {
247
- return method .getName () + getName ();
248
- }
249
-
250
- @ Override
251
- protected void validateConstructor (List <Throwable > errors ) {
252
- validateOnlyOneConstructor (errors );
253
- if (fieldsAreAnnotated ()) {
254
- validateZeroArgConstructor (errors );
255
- }
256
- }
257
-
258
- @ Override
259
- protected void validateFields (List <Throwable > errors ) {
260
- super .validateFields (errors );
261
- if (fieldsAreAnnotated ()) {
262
- List <FrameworkField > annotatedFieldsByParameter = getAnnotatedFieldsByParameter ();
263
- int [] usedIndices = new int [annotatedFieldsByParameter .size ()];
264
- for (FrameworkField each : annotatedFieldsByParameter ) {
265
- int index = each .getField ().getAnnotation (Parameter .class ).value ();
266
- if (index < 0 || index > annotatedFieldsByParameter .size () - 1 ) {
267
- errors .add (
268
- new Exception ("Invalid @Parameter value: " + index + ". @Parameter fields counted: " +
269
- annotatedFieldsByParameter .size () + ". Please use an index between 0 and " +
270
- (annotatedFieldsByParameter .size () - 1 ) + "." )
271
- );
272
- } else {
273
- usedIndices [index ]++;
274
- }
275
- }
276
- for (int index = 0 ; index < usedIndices .length ; index ++) {
277
- int numberOfUse = usedIndices [index ];
278
- if (numberOfUse == 0 ) {
279
- errors .add (new Exception ("@Parameter(" + index + ") is never used." ));
280
- } else if (numberOfUse > 1 ) {
281
- errors .add (new Exception ("@Parameter(" + index + ") is used more than once (" + numberOfUse + ")." ));
282
- }
283
- }
284
- }
285
- }
286
-
287
- @ Override
288
- protected Statement classBlock (RunNotifier notifier ) {
289
- return childrenInvoker (notifier );
290
- }
291
-
292
- @ Override
293
- protected Annotation [] getRunnerAnnotations () {
294
- return new Annotation [0 ];
295
- }
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 ;
296
229
}
297
230
231
+ private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory ();
232
+
298
233
private static final List <Runner > NO_RUNNERS = Collections .<Runner >emptyList ();
299
234
300
235
private final List <Runner > fRunners ;
@@ -304,26 +239,38 @@ protected Annotation[] getRunnerAnnotations() {
304
239
*/
305
240
public Parameterized (Class <?> klass ) throws Throwable {
306
241
super (klass , NO_RUNNERS );
242
+ ParametersRunnerFactory runnerFactory = getParametersRunnerFactory (
243
+ klass );
307
244
Parameters parameters = getParametersMethod ().getAnnotation (
308
245
Parameters .class );
309
- 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
+ }
310
261
}
311
262
312
263
@ Override
313
264
protected List <Runner > getChildren () {
314
265
return fRunners ;
315
266
}
316
267
317
- private Runner createRunnerWithNotNormalizedParameters (String pattern ,
318
- int index , Object parametersOrSingleParameter )
319
- throws InitializationError {
268
+ private TestWithParameters createTestWithNotNormalizedParameters (
269
+ String pattern , int index , Object parametersOrSingleParameter ) {
320
270
Object [] parameters = (parametersOrSingleParameter instanceof Object []) ? (Object []) parametersOrSingleParameter
321
271
: new Object [] { parametersOrSingleParameter };
322
- return createRunner (pattern , index , parameters );
323
- }
324
-
325
- protected Runner createRunner (String pattern , int index , Object [] parameters ) throws InitializationError {
326
- return new TestClassRunnerForParameters (getTestClass ().getJavaClass (), pattern , index , parameters );
272
+ return createTestWithParameters (getTestClass (), pattern , index ,
273
+ parameters );
327
274
}
328
275
329
276
@ SuppressWarnings ("unchecked" )
@@ -351,20 +298,37 @@ private FrameworkMethod getParametersMethod() throws Exception {
351
298
+ getTestClass ().getName ());
352
299
}
353
300
354
- 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 {
355
306
try {
356
- int i = 0 ;
357
- List <Runner > children = new ArrayList <Runner >();
358
- for (Object parametersOfSingleTest : allParameters ) {
359
- children .add (createRunnerWithNotNormalizedParameters (
360
- 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 ));
361
313
}
362
- return children ;
314
+ return runners ;
363
315
} catch (ClassCastException e ) {
364
316
throw parametersMethodReturnedWrongType ();
365
317
}
366
318
}
367
319
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
+
368
332
private Exception parametersMethodReturnedWrongType () throws Exception {
369
333
String className = getTestClass ().getName ();
370
334
String methodName = getParametersMethod ().getName ();
@@ -374,11 +338,12 @@ private Exception parametersMethodReturnedWrongType() throws Exception {
374
338
return new Exception (message );
375
339
}
376
340
377
- private List <FrameworkField > getAnnotatedFieldsByParameter () {
378
- return getTestClass ().getAnnotatedFields (Parameter .class );
379
- }
380
-
381
- private boolean fieldsAreAnnotated () {
382
- return !getAnnotatedFieldsByParameter ().isEmpty ();
341
+ private static TestWithParameters createTestWithParameters (
342
+ TestClass testClass , String pattern , int index , Object [] parameters ) {
343
+ String finalPattern = pattern .replaceAll ("\\ {index\\ }" ,
344
+ Integer .toString (index ));
345
+ String name = MessageFormat .format (finalPattern , parameters );
346
+ return new TestWithParameters ("[" + name + "]" , testClass ,
347
+ Arrays .asList (parameters ));
383
348
}
384
- }
349
+ }
0 commit comments