|
1 package org.junit.runners; |
|
2 |
|
3 import static org.junit.Assert.assertEquals; |
|
4 |
|
5 import java.lang.annotation.Annotation; |
|
6 import java.lang.annotation.ElementType; |
|
7 import java.lang.annotation.Retention; |
|
8 import java.lang.annotation.RetentionPolicy; |
|
9 import java.lang.annotation.Target; |
|
10 import java.lang.reflect.Constructor; |
|
11 import java.lang.reflect.InvocationTargetException; |
|
12 import java.lang.reflect.Method; |
|
13 import java.lang.reflect.Modifier; |
|
14 import java.util.ArrayList; |
|
15 import java.util.Collection; |
|
16 import java.util.List; |
|
17 |
|
18 import org.junit.internal.runners.CompositeRunner; |
|
19 import org.junit.internal.runners.MethodValidator; |
|
20 import org.junit.internal.runners.TestClassMethodsRunner; |
|
21 import org.junit.internal.runners.TestClassRunner; |
|
22 |
|
23 /** <p>The custom runner <code>Parameterized</code> implements parameterized |
|
24 * tests. When running a parameterized test class, instances are created for the |
|
25 * cross-product of the test methods and the test data elements.</p> |
|
26 * |
|
27 * For example, to test a Fibonacci function, write: |
|
28 * <pre> |
|
29 * @RunWith(Parameterized.class) |
|
30 * public class FibonacciTest { |
|
31 * @Parameters |
|
32 * public static Collection<Object[]> data() { |
|
33 * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, |
|
34 * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } }); |
|
35 * } |
|
36 * |
|
37 * private int fInput; |
|
38 * private int fExpected; |
|
39 * |
|
40 * public FibonacciTest(int input, int expected) { |
|
41 * fInput= input; |
|
42 * fExpected= expected; |
|
43 * } |
|
44 * |
|
45 * @Test public void test() { |
|
46 * assertEquals(fExpected, Fibonacci.compute(fInput)); |
|
47 * } |
|
48 * } |
|
49 * </pre> |
|
50 * |
|
51 * <p>Each instance of <code>FibonacciTest</code> will be constructed using the two-argument |
|
52 * constructor and the data values in the <code>@Parameters</code> method.</p> |
|
53 */ |
|
54 public class Parameterized extends TestClassRunner { |
|
55 @Retention(RetentionPolicy.RUNTIME) |
|
56 @Target(ElementType.METHOD) |
|
57 public static @interface Parameters { |
|
58 } |
|
59 |
|
60 public static Collection<Object[]> eachOne(Object... params) { |
|
61 List<Object[]> results= new ArrayList<Object[]>(); |
|
62 for (Object param : params) |
|
63 results.add(new Object[] { param }); |
|
64 return results; |
|
65 } |
|
66 |
|
67 // TODO: single-class this extension |
|
68 |
|
69 private static class TestClassRunnerForParameters extends TestClassMethodsRunner { |
|
70 private final Object[] fParameters; |
|
71 |
|
72 private final int fParameterSetNumber; |
|
73 |
|
74 private final Constructor<?> fConstructor; |
|
75 |
|
76 private TestClassRunnerForParameters(Class<?> klass, Object[] parameters, int i) { |
|
77 super(klass); |
|
78 fParameters= parameters; |
|
79 fParameterSetNumber= i; |
|
80 fConstructor= getOnlyConstructor(); |
|
81 } |
|
82 |
|
83 @Override |
|
84 protected Object createTest() throws Exception { |
|
85 return fConstructor.newInstance(fParameters); |
|
86 } |
|
87 |
|
88 @Override |
|
89 protected String getName() { |
|
90 return String.format("[%s]", fParameterSetNumber); |
|
91 } |
|
92 |
|
93 @Override |
|
94 protected String testName(final Method method) { |
|
95 return String.format("%s[%s]", method.getName(), fParameterSetNumber); |
|
96 } |
|
97 |
|
98 private Constructor<?> getOnlyConstructor() { |
|
99 Constructor<?>[] constructors= getTestClass().getConstructors(); |
|
100 assertEquals(1, constructors.length); |
|
101 return constructors[0]; |
|
102 } |
|
103 } |
|
104 |
|
105 // TODO: I think this now eagerly reads parameters, which was never the point. |
|
106 |
|
107 public static class RunAllParameterMethods extends CompositeRunner { |
|
108 private final Class<?> fKlass; |
|
109 |
|
110 public RunAllParameterMethods(Class<?> klass) throws Exception { |
|
111 super(klass.getName()); |
|
112 fKlass= klass; |
|
113 int i= 0; |
|
114 for (final Object each : getParametersList()) { |
|
115 if (each instanceof Object[]) |
|
116 super.add(new TestClassRunnerForParameters(klass, (Object[])each, i++)); |
|
117 else |
|
118 throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fKlass.getName(), getParametersMethod().getName())); |
|
119 } |
|
120 } |
|
121 |
|
122 private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception { |
|
123 return (Collection<?>) getParametersMethod().invoke(null); |
|
124 } |
|
125 |
|
126 private Method getParametersMethod() throws Exception { |
|
127 for (Method each : fKlass.getMethods()) { |
|
128 if (Modifier.isStatic(each.getModifiers())) { |
|
129 Annotation[] annotations= each.getAnnotations(); |
|
130 for (Annotation annotation : annotations) { |
|
131 if (annotation.annotationType() == Parameters.class) |
|
132 return each; |
|
133 } |
|
134 } |
|
135 } |
|
136 throw new Exception("No public static parameters method on class " |
|
137 + getName()); |
|
138 } |
|
139 } |
|
140 |
|
141 public Parameterized(final Class<?> klass) throws Exception { |
|
142 super(klass, new RunAllParameterMethods(klass)); |
|
143 } |
|
144 |
|
145 @Override |
|
146 protected void validate(MethodValidator methodValidator) { |
|
147 methodValidator.validateStaticMethods(); |
|
148 methodValidator.validateInstanceMethods(); |
|
149 } |
|
150 } |