001 package org.junit.runners; 002 003 import java.lang.annotation.ElementType; 004 import java.lang.annotation.Inherited; 005 import java.lang.annotation.Retention; 006 import java.lang.annotation.RetentionPolicy; 007 import java.lang.annotation.Target; 008 import java.text.MessageFormat; 009 import java.util.ArrayList; 010 import java.util.Arrays; 011 import java.util.Collections; 012 import java.util.List; 013 014 import org.junit.runner.Runner; 015 import org.junit.runners.model.FrameworkMethod; 016 import org.junit.runners.model.InitializationError; 017 import org.junit.runners.model.TestClass; 018 import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory; 019 import org.junit.runners.parameterized.ParametersRunnerFactory; 020 import org.junit.runners.parameterized.TestWithParameters; 021 022 /** 023 * The custom runner <code>Parameterized</code> implements parameterized tests. 024 * When running a parameterized test class, instances are created for the 025 * cross-product of the test methods and the test data elements. 026 * <p> 027 * For example, to test a Fibonacci function, write: 028 * <pre> 029 * @RunWith(Parameterized.class) 030 * public class FibonacciTest { 031 * @Parameters(name= "{index}: fib[{0}]={1}") 032 * public static Iterable<Object[]> data() { 033 * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, 034 * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } }); 035 * } 036 * 037 * private int fInput; 038 * 039 * private int fExpected; 040 * 041 * public FibonacciTest(int input, int expected) { 042 * fInput= input; 043 * fExpected= expected; 044 * } 045 * 046 * @Test 047 * public void test() { 048 * assertEquals(fExpected, Fibonacci.compute(fInput)); 049 * } 050 * } 051 * </pre> 052 * <p> 053 * Each instance of <code>FibonacciTest</code> will be constructed using the 054 * two-argument constructor and the data values in the 055 * <code>@Parameters</code> method. 056 * <p> 057 * In order that you can easily identify the individual tests, you may provide a 058 * name for the <code>@Parameters</code> annotation. This name is allowed 059 * to contain placeholders, which are replaced at runtime. The placeholders are 060 * <dl> 061 * <dt>{index}</dt> 062 * <dd>the current parameter index</dd> 063 * <dt>{0}</dt> 064 * <dd>the first parameter value</dd> 065 * <dt>{1}</dt> 066 * <dd>the second parameter value</dd> 067 * <dt>...</dt> 068 * <dd>...</dd> 069 * </dl> 070 * <p> 071 * In the example given above, the <code>Parameterized</code> runner creates 072 * names like <code>[1: fib(3)=2]</code>. If you don't use the name parameter, 073 * then the current parameter index is used as name. 074 * <p> 075 * You can also write: 076 * <pre> 077 * @RunWith(Parameterized.class) 078 * public class FibonacciTest { 079 * @Parameters 080 * public static Iterable<Object[]> data() { 081 * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, 082 * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } }); 083 * } 084 * 085 * @Parameter(0) 086 * public int fInput; 087 * 088 * @Parameter(1) 089 * public int fExpected; 090 * 091 * @Test 092 * public void test() { 093 * assertEquals(fExpected, Fibonacci.compute(fInput)); 094 * } 095 * } 096 * </pre> 097 * <p> 098 * Each instance of <code>FibonacciTest</code> will be constructed with the default constructor 099 * and fields annotated by <code>@Parameter</code> will be initialized 100 * with the data values in the <code>@Parameters</code> method. 101 * 102 * <p> 103 * The parameters can be provided as an array, too: 104 * 105 * <pre> 106 * @Parameters 107 * public static Object[][] data() { 108 * return new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, 109 * { 5, 5 }, { 6, 8 } }; 110 * } 111 * </pre> 112 * 113 * <h3>Tests with single parameter</h3> 114 * <p> 115 * If your test needs a single parameter only, you don't have to wrap it with an 116 * array. Instead you can provide an <code>Iterable</code> or an array of 117 * objects. 118 * <pre> 119 * @Parameters 120 * public static Iterable<? extends Object> data() { 121 * return Arrays.asList("first test", "second test"); 122 * } 123 * </pre> 124 * <p> 125 * or 126 * <pre> 127 * @Parameters 128 * public static Object[] data() { 129 * return new Object[] { "first test", "second test" }; 130 * } 131 * </pre> 132 * 133 * <h3>Create different runners</h3> 134 * <p> 135 * By default the {@code Parameterized} runner creates a slightly modified 136 * {@link BlockJUnit4ClassRunner} for each set of parameters. You can build an 137 * own {@code Parameterized} runner that creates another runner for each set of 138 * parameters. Therefore you have to build a {@link ParametersRunnerFactory} 139 * that creates a runner for each {@link TestWithParameters}. ( 140 * {@code TestWithParameters} are bundling the parameters and the test name.) 141 * The factory must have a public zero-arg constructor. 142 * 143 * <pre> 144 * public class YourRunnerFactory implements ParameterizedRunnerFactory { 145 * public Runner createRunnerForTestWithParameters(TestWithParameters test) 146 * throws InitializationError { 147 * return YourRunner(test); 148 * } 149 * } 150 * </pre> 151 * <p> 152 * Use the {@link UseParametersRunnerFactory} to tell the {@code Parameterized} 153 * runner that it should use your factory. 154 * 155 * <pre> 156 * @RunWith(Parameterized.class) 157 * @UseParametersRunnerFactory(YourRunnerFactory.class) 158 * public class YourTest { 159 * ... 160 * } 161 * </pre> 162 * 163 * @since 4.0 164 */ 165 public class Parameterized extends Suite { 166 /** 167 * Annotation for a method which provides parameters to be injected into the 168 * test class constructor by <code>Parameterized</code>. The method has to 169 * be public and static. 170 */ 171 @Retention(RetentionPolicy.RUNTIME) 172 @Target(ElementType.METHOD) 173 public static @interface Parameters { 174 /** 175 * Optional pattern to derive the test's name from the parameters. Use 176 * numbers in braces to refer to the parameters or the additional data 177 * as follows: 178 * <pre> 179 * {index} - the current parameter index 180 * {0} - the first parameter value 181 * {1} - the second parameter value 182 * etc... 183 * </pre> 184 * <p> 185 * Default value is "{index}" for compatibility with previous JUnit 186 * versions. 187 * 188 * @return {@link MessageFormat} pattern string, except the index 189 * placeholder. 190 * @see MessageFormat 191 */ 192 String name() default "{index}"; 193 } 194 195 /** 196 * Annotation for fields of the test class which will be initialized by the 197 * method annotated by <code>Parameters</code>. 198 * By using directly this annotation, the test class constructor isn't needed. 199 * Index range must start at 0. 200 * Default value is 0. 201 */ 202 @Retention(RetentionPolicy.RUNTIME) 203 @Target(ElementType.FIELD) 204 public static @interface Parameter { 205 /** 206 * Method that returns the index of the parameter in the array 207 * returned by the method annotated by <code>Parameters</code>. 208 * Index range must start at 0. 209 * Default value is 0. 210 * 211 * @return the index of the parameter. 212 */ 213 int value() default 0; 214 } 215 216 /** 217 * Add this annotation to your test class if you want to generate a special 218 * runner. You have to specify a {@link ParametersRunnerFactory} class that 219 * creates such runners. The factory must have a public zero-arg 220 * constructor. 221 */ 222 @Retention(RetentionPolicy.RUNTIME) 223 @Inherited 224 @Target(ElementType.TYPE) 225 public @interface UseParametersRunnerFactory { 226 /** 227 * @return a {@link ParametersRunnerFactory} class (must have a default 228 * constructor) 229 */ 230 Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class; 231 } 232 233 private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory(); 234 235 private static final List<Runner> NO_RUNNERS = Collections.<Runner>emptyList(); 236 237 private final List<Runner> runners; 238 239 /** 240 * Only called reflectively. Do not use programmatically. 241 */ 242 public Parameterized(Class<?> klass) throws Throwable { 243 super(klass, NO_RUNNERS); 244 ParametersRunnerFactory runnerFactory = getParametersRunnerFactory( 245 klass); 246 Parameters parameters = getParametersMethod().getAnnotation( 247 Parameters.class); 248 runners = Collections.unmodifiableList(createRunnersForParameters( 249 allParameters(), parameters.name(), runnerFactory)); 250 } 251 252 private ParametersRunnerFactory getParametersRunnerFactory(Class<?> klass) 253 throws InstantiationException, IllegalAccessException { 254 UseParametersRunnerFactory annotation = klass 255 .getAnnotation(UseParametersRunnerFactory.class); 256 if (annotation == null) { 257 return DEFAULT_FACTORY; 258 } else { 259 Class<? extends ParametersRunnerFactory> factoryClass = annotation 260 .value(); 261 return factoryClass.newInstance(); 262 } 263 } 264 265 @Override 266 protected List<Runner> getChildren() { 267 return runners; 268 } 269 270 private TestWithParameters createTestWithNotNormalizedParameters( 271 String pattern, int index, Object parametersOrSingleParameter) { 272 Object[] parameters= (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter 273 : new Object[] { parametersOrSingleParameter }; 274 return createTestWithParameters(getTestClass(), pattern, index, 275 parameters); 276 } 277 278 @SuppressWarnings("unchecked") 279 private Iterable<Object> allParameters() throws Throwable { 280 Object parameters = getParametersMethod().invokeExplosively(null); 281 if (parameters instanceof Iterable) { 282 return (Iterable<Object>) parameters; 283 } else if (parameters instanceof Object[]) { 284 return Arrays.asList((Object[]) parameters); 285 } else { 286 throw parametersMethodReturnedWrongType(); 287 } 288 } 289 290 private FrameworkMethod getParametersMethod() throws Exception { 291 List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods( 292 Parameters.class); 293 for (FrameworkMethod each : methods) { 294 if (each.isStatic() && each.isPublic()) { 295 return each; 296 } 297 } 298 299 throw new Exception("No public static parameters method on class " 300 + getTestClass().getName()); 301 } 302 303 private List<Runner> createRunnersForParameters( 304 Iterable<Object> allParameters, String namePattern, 305 ParametersRunnerFactory runnerFactory) 306 throws InitializationError, 307 Exception { 308 try { 309 List<TestWithParameters> tests = createTestsForParameters( 310 allParameters, namePattern); 311 List<Runner> runners = new ArrayList<Runner>(); 312 for (TestWithParameters test : tests) { 313 runners.add(runnerFactory 314 .createRunnerForTestWithParameters(test)); 315 } 316 return runners; 317 } catch (ClassCastException e) { 318 throw parametersMethodReturnedWrongType(); 319 } 320 } 321 322 private List<TestWithParameters> createTestsForParameters( 323 Iterable<Object> allParameters, String namePattern) 324 throws Exception { 325 int i = 0; 326 List<TestWithParameters> children = new ArrayList<TestWithParameters>(); 327 for (Object parametersOfSingleTest : allParameters) { 328 children.add(createTestWithNotNormalizedParameters(namePattern, 329 i++, parametersOfSingleTest)); 330 } 331 return children; 332 } 333 334 private Exception parametersMethodReturnedWrongType() throws Exception { 335 String className = getTestClass().getName(); 336 String methodName = getParametersMethod().getName(); 337 String message = MessageFormat.format( 338 "{0}.{1}() must return an Iterable of arrays.", 339 className, methodName); 340 return new Exception(message); 341 } 342 343 private static TestWithParameters createTestWithParameters( 344 TestClass testClass, String pattern, int index, Object[] parameters) { 345 String finalPattern = pattern.replaceAll("\\{index\\}", 346 Integer.toString(index)); 347 String name = MessageFormat.format(finalPattern, parameters); 348 return new TestWithParameters("[" + name + "]", testClass, 349 Arrays.asList(parameters)); 350 } 351 }