diff options
Diffstat (limited to 'src/main/java/org/junit/runners/Parameterized.java')
-rw-r--r-- | src/main/java/org/junit/runners/Parameterized.java | 405 |
1 files changed, 126 insertions, 279 deletions
diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java index d11b66a..829c8f0 100644 --- a/src/main/java/org/junit/runners/Parameterized.java +++ b/src/main/java/org/junit/runners/Parameterized.java @@ -1,6 +1,5 @@ package org.junit.runners; -import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; @@ -9,18 +8,12 @@ import java.lang.annotation.Target; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; -import org.junit.internal.AssumptionViolatedException; -import org.junit.runner.Description; -import org.junit.runner.Result; import org.junit.runner.Runner; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunNotifier; import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InvalidTestClassError; +import org.junit.runners.model.InitializationError; import org.junit.runners.model.TestClass; import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory; import org.junit.runners.parameterized.ParametersRunnerFactory; @@ -31,37 +24,34 @@ import org.junit.runners.parameterized.TestWithParameters; * When running a parameterized test class, instances are created for the * cross-product of the test methods and the test data elements. * <p> - * For example, to test the <code>+</code> operator, write: + * For example, to test a Fibonacci function, write: * <pre> * @RunWith(Parameterized.class) - * public class AdditionTest { - * @Parameters(name = "{index}: {0} + {1} = {2}") + * public class FibonacciTest { + * @Parameters(name= "{index}: fib[{0}]={1}") * public static Iterable<Object[]> data() { - * return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 }, - * { 3, 2, 5 }, { 4, 3, 7 } }); + * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, + * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } }); * } * - * private int firstSummand; + * private int fInput; * - * private int secondSummand; + * private int fExpected; * - * private int sum; - * - * public AdditionTest(int firstSummand, int secondSummand, int sum) { - * this.firstSummand = firstSummand; - * this.secondSummand = secondSummand; - * this.sum = sum; + * public FibonacciTest(int input, int expected) { + * fInput= input; + * fExpected= expected; * } * * @Test * public void test() { - * assertEquals(sum, firstSummand + secondSummand); + * assertEquals(fExpected, Fibonacci.compute(fInput)); * } * } * </pre> * <p> - * Each instance of <code>AdditionTest</code> will be constructed using the - * three-argument constructor and the data values in the + * Each instance of <code>FibonacciTest</code> will be constructed using the + * two-argument constructor and the data values in the * <code>@Parameters</code> method. * <p> * In order that you can easily identify the individual tests, you may provide a @@ -79,36 +69,33 @@ import org.junit.runners.parameterized.TestWithParameters; * </dl> * <p> * In the example given above, the <code>Parameterized</code> runner creates - * names like <code>[2: 3 + 2 = 5]</code>. If you don't use the name parameter, + * names like <code>[1: fib(3)=2]</code>. If you don't use the name parameter, * then the current parameter index is used as name. * <p> * You can also write: * <pre> * @RunWith(Parameterized.class) - * public class AdditionTest { - * @Parameters(name = "{index}: {0} + {1} = {2}") - * public static Iterable<Object[]> data() { - * return Arrays.asList(new Object[][] { { 0, 0, 0 }, { 1, 1, 2 }, - * { 3, 2, 5 }, { 4, 3, 7 } }); - * } - * - * @Parameter(0) - * public int firstSummand; + * public class FibonacciTest { + * @Parameters + * public static Iterable<Object[]> data() { + * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, + * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } }); + * } + * + * @Parameter(0) + * public int fInput; * - * @Parameter(1) - * public int secondSummand; + * @Parameter(1) + * public int fExpected; * - * @Parameter(2) - * public int sum; - * - * @Test - * public void test() { - * assertEquals(sum, firstSummand + secondSummand); - * } + * @Test + * public void test() { + * assertEquals(fExpected, Fibonacci.compute(fInput)); + * } * } * </pre> * <p> - * Each instance of <code>AdditionTest</code> will be constructed with the default constructor + * Each instance of <code>FibonacciTest</code> will be constructed with the default constructor * and fields annotated by <code>@Parameter</code> will be initialized * with the data values in the <code>@Parameters</code> method. * @@ -118,7 +105,8 @@ import org.junit.runners.parameterized.TestWithParameters; * <pre> * @Parameters * public static Object[][] data() { - * return new Object[][] { { 0, 0, 0 }, { 1, 1, 2 }, { 3, 2, 5 }, { 4, 3, 7 } } }; + * return new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, + * { 5, 5 }, { 6, 8 } }; * } * </pre> * @@ -142,19 +130,6 @@ import org.junit.runners.parameterized.TestWithParameters; * } * </pre> * - * <h3>Executing code before/after executing tests for specific parameters</h3> - * <p> - * If your test needs to perform some preparation or cleanup based on the - * parameters, this can be done by adding public static methods annotated with - * {@code @BeforeParam}/{@code @AfterParam}. Such methods should either have no - * parameters or the same parameters as the test. - * <pre> - * @BeforeParam - * public static void beforeTestsForParameter(String onlyParameter) { - * System.out.println("Testing " + onlyParameter); - * } - * </pre> - * * <h3>Create different runners</h3> * <p> * By default the {@code Parameterized} runner creates a slightly modified @@ -166,7 +141,7 @@ import org.junit.runners.parameterized.TestWithParameters; * The factory must have a public zero-arg constructor. * * <pre> - * public class YourRunnerFactory implements ParametersRunnerFactory { + * public class YourRunnerFactory implements ParameterizedRunnerFactory { * public Runner createRunnerForTestWithParameters(TestWithParameters test) * throws InitializationError { * return YourRunner(test); @@ -185,21 +160,6 @@ import org.junit.runners.parameterized.TestWithParameters; * } * </pre> * - * <h3>Avoid creating parameters</h3> - * <p>With {@link org.junit.Assume assumptions} you can dynamically skip tests. - * Assumptions are also supported by the <code>@Parameters</code> method. - * Creating parameters is stopped when the assumption fails and none of the - * tests in the test class is executed. JUnit reports a - * {@link Result#getAssumptionFailureCount() single assumption failure} for the - * whole test class in this case. - * <pre> - * @Parameters - * public static Iterable<? extends Object> data() { - * String os = System.getProperty("os.name").toLowerCase() - * Assume.assumeTrue(os.contains("win")); - * return Arrays.asList("first test", "second test"); - * } - * </pre> * @since 4.0 */ public class Parameterized extends Suite { @@ -210,7 +170,7 @@ public class Parameterized extends Suite { */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) - public @interface Parameters { + public static @interface Parameters { /** * Optional pattern to derive the test's name from the parameters. Use * numbers in braces to refer to the parameters or the additional data @@ -241,7 +201,7 @@ public class Parameterized extends Suite { */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) - public @interface Parameter { + public static @interface Parameter { /** * Method that returns the index of the parameter in the array * returned by the method annotated by <code>Parameters</code>. @@ -270,235 +230,122 @@ public class Parameterized extends Suite { Class<? extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class; } - /** - * Annotation for {@code public static void} methods which should be executed before - * evaluating tests with particular parameters. - * - * @see org.junit.BeforeClass - * @see org.junit.Before - * @since 4.13 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.METHOD) - public @interface BeforeParam { - } + private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory(); - /** - * Annotation for {@code public static void} methods which should be executed after - * evaluating tests with particular parameters. - * - * @see org.junit.AfterClass - * @see org.junit.After - * @since 4.13 - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.METHOD) - public @interface AfterParam { - } + private static final List<Runner> NO_RUNNERS = Collections.<Runner>emptyList(); + + private final List<Runner> runners; /** * Only called reflectively. Do not use programmatically. */ public Parameterized(Class<?> klass) throws Throwable { - this(klass, new RunnersFactory(klass)); - } - - private Parameterized(Class<?> klass, RunnersFactory runnersFactory) throws Exception { - super(klass, runnersFactory.createRunners()); - validateBeforeParamAndAfterParamMethods(runnersFactory.parameterCount); + super(klass, NO_RUNNERS); + ParametersRunnerFactory runnerFactory = getParametersRunnerFactory( + klass); + Parameters parameters = getParametersMethod().getAnnotation( + Parameters.class); + runners = Collections.unmodifiableList(createRunnersForParameters( + allParameters(), parameters.name(), runnerFactory)); } - private void validateBeforeParamAndAfterParamMethods(Integer parameterCount) - throws InvalidTestClassError { - List<Throwable> errors = new ArrayList<Throwable>(); - validatePublicStaticVoidMethods(Parameterized.BeforeParam.class, parameterCount, errors); - validatePublicStaticVoidMethods(Parameterized.AfterParam.class, parameterCount, errors); - if (!errors.isEmpty()) { - throw new InvalidTestClassError(getTestClass().getJavaClass(), errors); + private ParametersRunnerFactory getParametersRunnerFactory(Class<?> klass) + throws InstantiationException, IllegalAccessException { + UseParametersRunnerFactory annotation = klass + .getAnnotation(UseParametersRunnerFactory.class); + if (annotation == null) { + return DEFAULT_FACTORY; + } else { + Class<? extends ParametersRunnerFactory> factoryClass = annotation + .value(); + return factoryClass.newInstance(); } } - private void validatePublicStaticVoidMethods( - Class<? extends Annotation> annotation, Integer parameterCount, - List<Throwable> errors) { - List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(annotation); - for (FrameworkMethod fm : methods) { - fm.validatePublicVoid(true, errors); - if (parameterCount != null) { - int methodParameterCount = fm.getMethod().getParameterTypes().length; - if (methodParameterCount != 0 && methodParameterCount != parameterCount) { - errors.add(new Exception("Method " + fm.getName() - + "() should have 0 or " + parameterCount + " parameter(s)")); - } - } - } + @Override + protected List<Runner> getChildren() { + return runners; } - private static class AssumptionViolationRunner extends Runner { - private final Description description; - private final AssumptionViolatedException exception; - - AssumptionViolationRunner(TestClass testClass, String methodName, - AssumptionViolatedException exception) { - this.description = Description - .createTestDescription(testClass.getJavaClass(), - methodName + "() assumption violation"); - this.exception = exception; - } - - @Override - public Description getDescription() { - return description; - } - - @Override - public void run(RunNotifier notifier) { - notifier.fireTestAssumptionFailed(new Failure(description, exception)); - } + private TestWithParameters createTestWithNotNormalizedParameters( + String pattern, int index, Object parametersOrSingleParameter) { + Object[] parameters= (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter + : new Object[] { parametersOrSingleParameter }; + return createTestWithParameters(getTestClass(), pattern, index, + parameters); } - private static class RunnersFactory { - private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory(); - - private final TestClass testClass; - private final FrameworkMethod parametersMethod; - private final List<Object> allParameters; - private final int parameterCount; - private final Runner runnerOverride; - - private RunnersFactory(Class<?> klass) throws Throwable { - testClass = new TestClass(klass); - parametersMethod = getParametersMethod(testClass); - List<Object> allParametersResult; - AssumptionViolationRunner assumptionViolationRunner = null; - try { - allParametersResult = allParameters(testClass, parametersMethod); - } catch (AssumptionViolatedException e) { - allParametersResult = Collections.emptyList(); - assumptionViolationRunner = new AssumptionViolationRunner(testClass, - parametersMethod.getName(), e); - } - allParameters = allParametersResult; - runnerOverride = assumptionViolationRunner; - parameterCount = - allParameters.isEmpty() ? 0 : normalizeParameters(allParameters.get(0)).length; - } - - private List<Runner> createRunners() throws Exception { - if (runnerOverride != null) { - return Collections.singletonList(runnerOverride); - } - Parameters parameters = parametersMethod.getAnnotation(Parameters.class); - return Collections.unmodifiableList(createRunnersForParameters( - allParameters, parameters.name(), - getParametersRunnerFactory())); - } - - private ParametersRunnerFactory getParametersRunnerFactory() - throws InstantiationException, IllegalAccessException { - UseParametersRunnerFactory annotation = testClass - .getAnnotation(UseParametersRunnerFactory.class); - if (annotation == null) { - return DEFAULT_FACTORY; - } else { - Class<? extends ParametersRunnerFactory> factoryClass = annotation - .value(); - return factoryClass.newInstance(); - } - } - - private TestWithParameters createTestWithNotNormalizedParameters( - String pattern, int index, Object parametersOrSingleParameter) { - Object[] parameters = normalizeParameters(parametersOrSingleParameter); - return createTestWithParameters(testClass, pattern, index, parameters); - } - - private static Object[] normalizeParameters(Object parametersOrSingleParameter) { - return (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter - : new Object[] { parametersOrSingleParameter }; + @SuppressWarnings("unchecked") + private Iterable<Object> allParameters() throws Throwable { + Object parameters = getParametersMethod().invokeExplosively(null); + if (parameters instanceof Iterable) { + return (Iterable<Object>) parameters; + } else if (parameters instanceof Object[]) { + return Arrays.asList((Object[]) parameters); + } else { + throw parametersMethodReturnedWrongType(); } + } - @SuppressWarnings("unchecked") - private static List<Object> allParameters( - TestClass testClass, FrameworkMethod parametersMethod) throws Throwable { - Object parameters = parametersMethod.invokeExplosively(null); - if (parameters instanceof List) { - return (List<Object>) parameters; - } else if (parameters instanceof Collection) { - return new ArrayList<Object>((Collection<Object>) parameters); - } else if (parameters instanceof Iterable) { - List<Object> result = new ArrayList<Object>(); - for (Object entry : ((Iterable<Object>) parameters)) { - result.add(entry); - } - return result; - } else if (parameters instanceof Object[]) { - return Arrays.asList((Object[]) parameters); - } else { - throw parametersMethodReturnedWrongType(testClass, parametersMethod); + private FrameworkMethod getParametersMethod() throws Exception { + List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods( + Parameters.class); + for (FrameworkMethod each : methods) { + if (each.isStatic() && each.isPublic()) { + return each; } } - private static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { - List<FrameworkMethod> methods = testClass - .getAnnotatedMethods(Parameters.class); - for (FrameworkMethod each : methods) { - if (each.isStatic() && each.isPublic()) { - return each; - } - } - - throw new Exception("No public static parameters method on class " - + testClass.getName()); - } + throw new Exception("No public static parameters method on class " + + getTestClass().getName()); + } - private List<Runner> createRunnersForParameters( - Iterable<Object> allParameters, String namePattern, - ParametersRunnerFactory runnerFactory) throws Exception { - try { - List<TestWithParameters> tests = createTestsForParameters( - allParameters, namePattern); - List<Runner> runners = new ArrayList<Runner>(); - for (TestWithParameters test : tests) { - runners.add(runnerFactory - .createRunnerForTestWithParameters(test)); - } - return runners; - } catch (ClassCastException e) { - throw parametersMethodReturnedWrongType(testClass, parametersMethod); + private List<Runner> createRunnersForParameters( + Iterable<Object> allParameters, String namePattern, + ParametersRunnerFactory runnerFactory) + throws InitializationError, + Exception { + try { + List<TestWithParameters> tests = createTestsForParameters( + allParameters, namePattern); + List<Runner> runners = new ArrayList<Runner>(); + for (TestWithParameters test : tests) { + runners.add(runnerFactory + .createRunnerForTestWithParameters(test)); } + return runners; + } catch (ClassCastException e) { + throw parametersMethodReturnedWrongType(); } + } - private List<TestWithParameters> createTestsForParameters( - Iterable<Object> allParameters, String namePattern) - throws Exception { - int i = 0; - List<TestWithParameters> children = new ArrayList<TestWithParameters>(); - for (Object parametersOfSingleTest : allParameters) { - children.add(createTestWithNotNormalizedParameters(namePattern, - i++, parametersOfSingleTest)); - } - return children; + private List<TestWithParameters> createTestsForParameters( + Iterable<Object> allParameters, String namePattern) + throws Exception { + int i = 0; + List<TestWithParameters> children = new ArrayList<TestWithParameters>(); + for (Object parametersOfSingleTest : allParameters) { + children.add(createTestWithNotNormalizedParameters(namePattern, + i++, parametersOfSingleTest)); } + return children; + } - private static Exception parametersMethodReturnedWrongType( - TestClass testClass, FrameworkMethod parametersMethod) throws Exception { - String className = testClass.getName(); - String methodName = parametersMethod.getName(); - String message = MessageFormat.format( - "{0}.{1}() must return an Iterable of arrays.", className, - methodName); - return new Exception(message); - } + private Exception parametersMethodReturnedWrongType() throws Exception { + String className = getTestClass().getName(); + String methodName = getParametersMethod().getName(); + String message = MessageFormat.format( + "{0}.{1}() must return an Iterable of arrays.", + className, methodName); + return new Exception(message); + } - private TestWithParameters createTestWithParameters( - TestClass testClass, String pattern, int index, - Object[] parameters) { - String finalPattern = pattern.replaceAll("\\{index\\}", - Integer.toString(index)); - String name = MessageFormat.format(finalPattern, parameters); - return new TestWithParameters("[" + name + "]", testClass, - Arrays.asList(parameters)); - } + private static TestWithParameters createTestWithParameters( + TestClass testClass, String pattern, int index, Object[] parameters) { + String finalPattern = pattern.replaceAll("\\{index\\}", + Integer.toString(index)); + String name = MessageFormat.format(finalPattern, parameters); + return new TestWithParameters("[" + name + "]", testClass, + Arrays.asList(parameters)); } } |