• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.junit.runners.parameterized;
2 
3 import java.lang.annotation.Annotation;
4 import java.lang.reflect.Field;
5 import java.util.List;
6 
7 import org.junit.internal.runners.statements.RunAfters;
8 import org.junit.internal.runners.statements.RunBefores;
9 import org.junit.runner.RunWith;
10 import org.junit.runner.notification.RunNotifier;
11 import org.junit.runners.BlockJUnit4ClassRunner;
12 import org.junit.runners.Parameterized;
13 import org.junit.runners.Parameterized.Parameter;
14 import org.junit.runners.model.FrameworkField;
15 import org.junit.runners.model.FrameworkMethod;
16 import org.junit.runners.model.InitializationError;
17 import org.junit.runners.model.Statement;
18 
19 /**
20  * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be
21  * injected via constructor or into annotated fields.
22  */
23 public class BlockJUnit4ClassRunnerWithParameters extends
24         BlockJUnit4ClassRunner {
25     private enum InjectionType {
26         CONSTRUCTOR, FIELD
27     }
28 
29     private final Object[] parameters;
30 
31     private final String name;
32 
BlockJUnit4ClassRunnerWithParameters(TestWithParameters test)33     public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test)
34             throws InitializationError {
35         super(test.getTestClass());
36         parameters = test.getParameters().toArray(
37                 new Object[test.getParameters().size()]);
38         name = test.getName();
39     }
40 
41     @Override
createTest()42     public Object createTest() throws Exception {
43         InjectionType injectionType = getInjectionType();
44         switch (injectionType) {
45             case CONSTRUCTOR:
46                 return createTestUsingConstructorInjection();
47             case FIELD:
48                 return createTestUsingFieldInjection();
49             default:
50                 throw new IllegalStateException("The injection type "
51                         + injectionType + " is not supported.");
52         }
53     }
54 
createTestUsingConstructorInjection()55     private Object createTestUsingConstructorInjection() throws Exception {
56         return getTestClass().getOnlyConstructor().newInstance(parameters);
57     }
58 
createTestUsingFieldInjection()59     private Object createTestUsingFieldInjection() throws Exception {
60         List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
61         if (annotatedFieldsByParameter.size() != parameters.length) {
62             throw new Exception(
63                     "Wrong number of parameters and @Parameter fields."
64                             + " @Parameter fields counted: "
65                             + annotatedFieldsByParameter.size()
66                             + ", available parameters: " + parameters.length
67                             + ".");
68         }
69         Object testClassInstance = getTestClass().getJavaClass().newInstance();
70         for (FrameworkField each : annotatedFieldsByParameter) {
71             Field field = each.getField();
72             Parameter annotation = field.getAnnotation(Parameter.class);
73             int index = annotation.value();
74             try {
75                 field.set(testClassInstance, parameters[index]);
76             } catch (IllegalAccessException e) {
77                 IllegalAccessException wrappedException = new IllegalAccessException(
78                         "Cannot set parameter '" + field.getName()
79                                 + "'. Ensure that the field '" + field.getName()
80                                 + "' is public.");
81                 wrappedException.initCause(e);
82                 throw wrappedException;
83             } catch (IllegalArgumentException iare) {
84                 throw new Exception(getTestClass().getName()
85                         + ": Trying to set " + field.getName()
86                         + " with the value " + parameters[index]
87                         + " that is not the right type ("
88                         + parameters[index].getClass().getSimpleName()
89                         + " instead of " + field.getType().getSimpleName()
90                         + ").", iare);
91             }
92         }
93         return testClassInstance;
94     }
95 
96     @Override
getName()97     protected String getName() {
98         return name;
99     }
100 
101     @Override
testName(FrameworkMethod method)102     protected String testName(FrameworkMethod method) {
103         return method.getName() + getName();
104     }
105 
106     @Override
validateConstructor(List<Throwable> errors)107     protected void validateConstructor(List<Throwable> errors) {
108         validateOnlyOneConstructor(errors);
109         if (getInjectionType() != InjectionType.CONSTRUCTOR) {
110             validateZeroArgConstructor(errors);
111         }
112     }
113 
114     @Override
validateFields(List<Throwable> errors)115     protected void validateFields(List<Throwable> errors) {
116         super.validateFields(errors);
117         if (getInjectionType() == InjectionType.FIELD) {
118             List<FrameworkField> annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
119             int[] usedIndices = new int[annotatedFieldsByParameter.size()];
120             for (FrameworkField each : annotatedFieldsByParameter) {
121                 int index = each.getField().getAnnotation(Parameter.class)
122                         .value();
123                 if (index < 0 || index > annotatedFieldsByParameter.size() - 1) {
124                     errors.add(new Exception("Invalid @Parameter value: "
125                             + index + ". @Parameter fields counted: "
126                             + annotatedFieldsByParameter.size()
127                             + ". Please use an index between 0 and "
128                             + (annotatedFieldsByParameter.size() - 1) + "."));
129                 } else {
130                     usedIndices[index]++;
131                 }
132             }
133             for (int index = 0; index < usedIndices.length; index++) {
134                 int numberOfUse = usedIndices[index];
135                 if (numberOfUse == 0) {
136                     errors.add(new Exception("@Parameter(" + index
137                             + ") is never used."));
138                 } else if (numberOfUse > 1) {
139                     errors.add(new Exception("@Parameter(" + index
140                             + ") is used more than once (" + numberOfUse + ")."));
141                 }
142             }
143         }
144     }
145 
146     @Override
classBlock(RunNotifier notifier)147     protected Statement classBlock(RunNotifier notifier) {
148         Statement statement = childrenInvoker(notifier);
149         statement = withBeforeParams(statement);
150         statement = withAfterParams(statement);
151         return statement;
152     }
153 
withBeforeParams(Statement statement)154     private Statement withBeforeParams(Statement statement) {
155         List<FrameworkMethod> befores = getTestClass()
156                 .getAnnotatedMethods(Parameterized.BeforeParam.class);
157         return befores.isEmpty() ? statement : new RunBeforeParams(statement, befores);
158     }
159 
160     private class RunBeforeParams extends RunBefores {
RunBeforeParams(Statement next, List<FrameworkMethod> befores)161         RunBeforeParams(Statement next, List<FrameworkMethod> befores) {
162             super(next, befores, null);
163         }
164 
165         @Override
invokeMethod(FrameworkMethod method)166         protected void invokeMethod(FrameworkMethod method) throws Throwable {
167             int paramCount = method.getMethod().getParameterTypes().length;
168             method.invokeExplosively(null, paramCount == 0 ? (Object[]) null : parameters);
169         }
170     }
171 
withAfterParams(Statement statement)172     private Statement withAfterParams(Statement statement) {
173         List<FrameworkMethod> afters = getTestClass()
174                 .getAnnotatedMethods(Parameterized.AfterParam.class);
175         return afters.isEmpty() ? statement : new RunAfterParams(statement, afters);
176     }
177 
178     private class RunAfterParams extends RunAfters {
RunAfterParams(Statement next, List<FrameworkMethod> afters)179         RunAfterParams(Statement next, List<FrameworkMethod> afters) {
180             super(next, afters, null);
181         }
182 
183         @Override
invokeMethod(FrameworkMethod method)184         protected void invokeMethod(FrameworkMethod method) throws Throwable {
185             int paramCount = method.getMethod().getParameterTypes().length;
186             method.invokeExplosively(null, paramCount == 0 ? (Object[]) null : parameters);
187         }
188     }
189 
190     @Override
getRunnerAnnotations()191     protected Annotation[] getRunnerAnnotations() {
192         Annotation[] allAnnotations = super.getRunnerAnnotations();
193         Annotation[] annotationsWithoutRunWith = new Annotation[allAnnotations.length - 1];
194         int i = 0;
195         for (Annotation annotation: allAnnotations) {
196             if (!annotation.annotationType().equals(RunWith.class)) {
197                 annotationsWithoutRunWith[i] = annotation;
198                 ++i;
199             }
200         }
201         return annotationsWithoutRunWith;
202     }
203 
getAnnotatedFieldsByParameter()204     private List<FrameworkField> getAnnotatedFieldsByParameter() {
205         return getTestClass().getAnnotatedFields(Parameter.class);
206     }
207 
getInjectionType()208     private InjectionType getInjectionType() {
209         if (fieldsAreAnnotated()) {
210             return InjectionType.FIELD;
211         } else {
212             return InjectionType.CONSTRUCTOR;
213         }
214     }
215 
fieldsAreAnnotated()216     private boolean fieldsAreAnnotated() {
217         return !getAnnotatedFieldsByParameter().isEmpty();
218     }
219 }
220