package org.junit.runners; import static org.junit.Assert.assertEquals; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.junit.internal.runners.CompositeRunner; import org.junit.internal.runners.MethodValidator; import org.junit.internal.runners.TestClassMethodsRunner; import org.junit.internal.runners.TestClassRunner; /** The custom runner <code>Parameterized</code> implements parameterized * tests. When running a parameterized test class, instances are created for the * cross-product of the test methods and the test data elements.<br> * <p> * For example, to test a Fibonacci function, write: * <code> *  <br>@RunWith(Parameterized.class)<br> * public class FibonacciTest {<br> *   @Parameters<br> *   public static Collection<Object[]> data() {<br> *     return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },<br> *       { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });<br> *   }<br> *<br> *   private int fInput;<br> *   private int fExpected;<br> *<br> *   public FibonacciTest(int input, int expected) {<br> *     fInput= input;<br> *     fExpected= expected;<br> *   }<br> *<br> *   @Test public void test() {<br> *     assertEquals(fExpected, Fibonacci.compute(fInput));<br> *   }<br> * }<br> * </code> * <p> * Each instance of <code>FibonacciTest</code> will be constructed using the two-argument * constructor and the data values in the <code>@Parameters</code> method. */ public class Parameterized extends TestClassRunner { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public static @interface Parameters { } public static Collection<Object[]> eachOne(Object... params) { List<Object[]> results= new ArrayList<Object[]>(); for (Object param : params) results.add(new Object[] { param }); return results; } // TODO: single-class this extension private static class TestClassRunnerForParameters extends TestClassMethodsRunner { private final Object[] fParameters; private final int fParameterSetNumber; private final Constructor fConstructor; private TestClassRunnerForParameters(Class<?> klass, Object[] parameters, int i) { super(klass); fParameters= parameters; fParameterSetNumber= i; fConstructor= getOnlyConstructor(); } @Override protected Object createTest() throws Exception { return fConstructor.newInstance(fParameters); } @Override protected String getName() { return String.format("[%s]", fParameterSetNumber); } @Override protected String testName(final Method method) { return String.format("%s[%s]", method.getName(), fParameterSetNumber); } private Constructor getOnlyConstructor() { Constructor[] constructors= getTestClass().getConstructors(); assertEquals(1, constructors.length); return constructors[0]; } } // TODO: I think this now eagerly reads parameters, which was never the point. public static class RunAllParameterMethods extends CompositeRunner { private final Class<?> fKlass; public RunAllParameterMethods(Class<?> klass) throws Exception { super(klass.getName()); fKlass= klass; int i= 0; for (final Object[] parameters : getParametersList()) { super.add(new TestClassRunnerForParameters(klass, parameters, i++)); } } @SuppressWarnings("unchecked") private Collection<Object[]> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception { return (Collection) getParametersMethod().invoke(null); } private Method getParametersMethod() throws Exception { for (Method each : fKlass.getMethods()) { if (Modifier.isStatic(each.getModifiers())) { Annotation[] annotations= each.getAnnotations(); for (Annotation annotation : annotations) { if (annotation.annotationType() == Parameters.class) return each; } } } throw new Exception("No public static parameters method on class " + getName()); } } public Parameterized(final Class<?> klass) throws Exception { super(klass, new RunAllParameterMethods(klass)); } @Override protected void validate(MethodValidator methodValidator) { methodValidator.validateStaticMethods(); methodValidator.validateInstanceMethods(); } }