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 * @RunWith(Parameterized.class) 31 * public class FibonacciTest { 32 * @Parameters 33 * public static List<Object[]> 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 * @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>@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