• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.junit.runners;
2 
3 import java.lang.annotation.Annotation;
4 import java.lang.annotation.ElementType;
5 import java.lang.annotation.Retention;
6 import java.lang.annotation.RetentionPolicy;
7 import java.lang.annotation.Target;
8 import java.lang.reflect.Modifier;
9 import java.util.ArrayList;
10 import java.util.Collections;
11 import java.util.List;
12 
13 import org.junit.runner.Runner;
14 import org.junit.runner.notification.RunNotifier;
15 import org.junit.runners.model.FrameworkMethod;
16 import org.junit.runners.model.InitializationError;
17 import org.junit.runners.model.Statement;
18 import org.junit.runners.model.TestClass;
19 
20 /**
21  * <p>
22  * The custom runner <code>Parameterized</code> implements parameterized tests.
23  * When running a parameterized test class, instances are created for the
24  * cross-product of the test methods and the test data elements.
25  * </p>
26  *
27  * For example, to test a Fibonacci function, write:
28  *
29  * <pre>
30  * &#064;RunWith(Parameterized.class)
31  * public class FibonacciTest {
32  * 	&#064;Parameters
33  * 	public static List&lt;Object[]&gt; data() {
34  * 		return Arrays.asList(new Object[][] {
35  * 			{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
36  * 		});
37  * 	}
38  *
39  * 	private int fInput;
40  *
41  * 	private int fExpected;
42  *
43  * 	public FibonacciTest(int input, int expected) {
44  * 		fInput= input;
45  * 		fExpected= expected;
46  * 	}
47  *
48  * 	&#064;Test
49  * 	public void test() {
50  * 		assertEquals(fExpected, Fibonacci.compute(fInput));
51  * 	}
52  * }
53  * </pre>
54  *
55  * <p>
56  * Each instance of <code>FibonacciTest</code> will be constructed using the
57  * two-argument constructor and the data values in the
58  * <code>&#064;Parameters</code> method.
59  * </p>
60  */
61 public class Parameterized extends Suite {
62 	/**
63 	 * Annotation for a method which provides parameters to be injected into the
64 	 * test class constructor by <code>Parameterized</code>
65 	 */
66 	@Retention(RetentionPolicy.RUNTIME)
67 	@Target(ElementType.METHOD)
68 	public static @interface Parameters {
69 	}
70 
71 	private class TestClassRunnerForParameters extends
72 			BlockJUnit4ClassRunner {
73 		private final int fParameterSetNumber;
74 
75 		private final List<Object[]> fParameterList;
76 
TestClassRunnerForParameters(Class<?> type, List<Object[]> parameterList, int i)77 		TestClassRunnerForParameters(Class<?> type,
78 				List<Object[]> parameterList, int i) throws InitializationError {
79 			super(type);
80 			fParameterList= parameterList;
81 			fParameterSetNumber= i;
82 		}
83 
84 		@Override
createTest()85 		public Object createTest() throws Exception {
86 			return getTestClass().getOnlyConstructor().newInstance(
87 					computeParams());
88 		}
89 
computeParams()90 		private Object[] computeParams() throws Exception {
91 			try {
92 				return fParameterList.get(fParameterSetNumber);
93 			} catch (ClassCastException e) {
94 				throw new Exception(String.format(
95 						"%s.%s() must return a Collection of arrays.",
96 						getTestClass().getName(), getParametersMethod(
97 								getTestClass()).getName()));
98 			}
99 		}
100 
101 		@Override
getName()102 		protected String getName() {
103 			return String.format("[%s]", fParameterSetNumber);
104 		}
105 
106 		@Override
testName(final FrameworkMethod method)107 		protected String testName(final FrameworkMethod method) {
108 			return String.format("%s[%s]", method.getName(),
109 					fParameterSetNumber);
110 		}
111 
112 		@Override
validateConstructor(List<Throwable> errors)113 		protected void validateConstructor(List<Throwable> errors) {
114 			validateOnlyOneConstructor(errors);
115 		}
116 
117 		@Override
classBlock(RunNotifier notifier)118 		protected Statement classBlock(RunNotifier notifier) {
119 			return childrenInvoker(notifier);
120 		}
121 
122 		@Override
getRunnerAnnotations()123 		protected Annotation[] getRunnerAnnotations() {
124 			return new Annotation[0];
125 		}
126 	}
127 
128 	private final ArrayList<Runner> runners= new ArrayList<Runner>();
129 
130 	/**
131 	 * Only called reflectively. Do not use programmatically.
132 	 */
Parameterized(Class<?> klass)133 	public Parameterized(Class<?> klass) throws Throwable {
134 		super(klass, Collections.<Runner>emptyList());
135 		List<Object[]> parametersList= getParametersList(getTestClass());
136 		for (int i= 0; i < parametersList.size(); i++)
137 			runners.add(new TestClassRunnerForParameters(getTestClass().getJavaClass(),
138 					parametersList, i));
139 	}
140 
141 	@Override
getChildren()142 	protected List<Runner> getChildren() {
143 		return runners;
144 	}
145 
146 	@SuppressWarnings("unchecked")
getParametersList(TestClass klass)147 	private List<Object[]> getParametersList(TestClass klass)
148 			throws Throwable {
149 		return (List<Object[]>) getParametersMethod(klass).invokeExplosively(
150 				null);
151 	}
152 
getParametersMethod(TestClass testClass)153 	private FrameworkMethod getParametersMethod(TestClass testClass)
154 			throws Exception {
155 		List<FrameworkMethod> methods= testClass
156 				.getAnnotatedMethods(Parameters.class);
157 		for (FrameworkMethod each : methods) {
158 			int modifiers= each.getMethod().getModifiers();
159 			if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))
160 				return each;
161 		}
162 
163 		throw new Exception("No public static parameters method on class "
164 				+ testClass.getName());
165 	}
166 
167 }
168