org.junit.runners.Parameterized Maven / Gradle / Ivy
package org.junit.runners;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.runner.Runner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.TestClass;
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory;
import org.junit.runners.parameterized.ParametersRunnerFactory;
import org.junit.runners.parameterized.TestWithParameters;
/**
* The custom runner Parameterized
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.
*
* For example, to test a Fibonacci function, write:
*
* @RunWith(Parameterized.class)
* public class FibonacciTest {
* @Parameters(name= "{index}: fib[{0}]={1}")
* public static Iterable<Object[]> data() {
* return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
* { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
* }
*
* private int fInput;
*
* private int fExpected;
*
* public FibonacciTest(int input, int expected) {
* fInput= input;
* fExpected= expected;
* }
*
* @Test
* public void test() {
* assertEquals(fExpected, Fibonacci.compute(fInput));
* }
* }
*
*
* Each instance of FibonacciTest
will be constructed using the
* two-argument constructor and the data values in the
* @Parameters
method.
*
* In order that you can easily identify the individual tests, you may provide a
* name for the @Parameters
annotation. This name is allowed
* to contain placeholders, which are replaced at runtime. The placeholders are
*
* - {index}
* - the current parameter index
* - {0}
* - the first parameter value
* - {1}
* - the second parameter value
* - ...
* - ...
*
*
* In the example given above, the Parameterized
runner creates
* names like [1: fib(3)=2]
. If you don't use the name parameter,
* then the current parameter index is used as name.
*
* You can also write:
*
* @RunWith(Parameterized.class)
* 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 fExpected;
*
* @Test
* public void test() {
* assertEquals(fExpected, Fibonacci.compute(fInput));
* }
* }
*
*
* Each instance of FibonacciTest
will be constructed with the default constructor
* and fields annotated by @Parameter
will be initialized
* with the data values in the @Parameters
method.
*
*
* The parameters can be provided as an array, too:
*
*
* @Parameters
* public static Object[][] data() {
* return new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 },
* { 5, 5 }, { 6, 8 } };
* }
*
*
* Tests with single parameter
*
* If your test needs a single parameter only, you don't have to wrap it with an
* array. Instead you can provide an Iterable
or an array of
* objects.
*
* @Parameters
* public static Iterable<? extends Object> data() {
* return Arrays.asList("first test", "second test");
* }
*
*
* or
*
* @Parameters
* public static Object[] data() {
* return new Object[] { "first test", "second test" };
* }
*
*
* Create different runners
*
* By default the {@code Parameterized} runner creates a slightly modified
* {@link BlockJUnit4ClassRunner} for each set of parameters. You can build an
* own {@code Parameterized} runner that creates another runner for each set of
* parameters. Therefore you have to build a {@link ParametersRunnerFactory}
* that creates a runner for each {@link TestWithParameters}. (
* {@code TestWithParameters} are bundling the parameters and the test name.)
* The factory must have a public zero-arg constructor.
*
*
* public class YourRunnerFactory implements ParameterizedRunnerFactory {
* public Runner createRunnerForTestWithParameters(TestWithParameters test)
* throws InitializationError {
* return YourRunner(test);
* }
* }
*
*
* Use the {@link UseParametersRunnerFactory} to tell the {@code Parameterized}
* runner that it should use your factory.
*
*
* @RunWith(Parameterized.class)
* @UseParametersRunnerFactory(YourRunnerFactory.class)
* public class YourTest {
* ...
* }
*
*
* @since 4.0
*/
public class Parameterized extends Suite {
/**
* Annotation for a method which provides parameters to be injected into the
* test class constructor by Parameterized
. The method has to
* be public and static.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
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
* as follows:
*
* {index} - the current parameter index
* {0} - the first parameter value
* {1} - the second parameter value
* etc...
*
*
* Default value is "{index}" for compatibility with previous JUnit
* versions.
*
* @return {@link MessageFormat} pattern string, except the index
* placeholder.
* @see MessageFormat
*/
String name() default "{index}";
}
/**
* Annotation for fields of the test class which will be initialized by the
* method annotated by Parameters
.
* By using directly this annotation, the test class constructor isn't needed.
* Index range must start at 0.
* Default value is 0.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public static @interface Parameter {
/**
* Method that returns the index of the parameter in the array
* returned by the method annotated by Parameters
.
* Index range must start at 0.
* Default value is 0.
*
* @return the index of the parameter.
*/
int value() default 0;
}
/**
* Add this annotation to your test class if you want to generate a special
* runner. You have to specify a {@link ParametersRunnerFactory} class that
* creates such runners. The factory must have a public zero-arg
* constructor.
*/
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.TYPE)
public @interface UseParametersRunnerFactory {
/**
* @return a {@link ParametersRunnerFactory} class (must have a default
* constructor)
*/
Class extends ParametersRunnerFactory> value() default BlockJUnit4ClassRunnerWithParametersFactory.class;
}
private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory();
private static final List NO_RUNNERS = Collections.emptyList();
private final List runners;
/**
* Only called reflectively. Do not use programmatically.
*/
public Parameterized(Class> klass) throws Throwable {
super(klass, NO_RUNNERS);
ParametersRunnerFactory runnerFactory = getParametersRunnerFactory(
klass);
Parameters parameters = getParametersMethod().getAnnotation(
Parameters.class);
runners = Collections.unmodifiableList(createRunnersForParameters(
allParameters(), parameters.name(), runnerFactory));
}
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();
}
}
@Override
protected List getChildren() {
return runners;
}
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);
}
@SuppressWarnings("unchecked")
private Iterable