package org.junit.runners;
import java.lang.annotation.Annotation;
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.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.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 the +
operator, write:
*
* @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 } }); * } * * private int firstSummand; * * private int secondSummand; * * private int sum; * * public AdditionTest(int firstSummand, int secondSummand, int sum) { * this.firstSummand = firstSummand; * this.secondSummand = secondSummand; * this.sum = sum; * } * * @Test * public void test() { * assertEquals(sum, firstSummand + secondSummand); * } * } **
* Each instance of AdditionTest
will be constructed using the
* three-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
*
* In the example given above, the Parameterized
runner creates
* names like [2: 3 + 2 = 5]
. 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 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; * * @Parameter(1) * public int secondSummand; * * @Parameter(2) * public int sum; * * @Test * public void test() { * assertEquals(sum, firstSummand + secondSummand); * } * } **
* Each instance of AdditionTest
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, 0 }, { 1, 1, 2 }, { 3, 2, 5 }, { 4, 3, 7 } } }; * } ** *
* 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" }; * } ** *
* 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. *
* @BeforeParam * public static void beforeTestsForParameter(String onlyParameter) { * System.out.println("Testing " + onlyParameter); * } ** *
* 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 ParametersRunnerFactory { * 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 { * ... * } ** *
With {@link org.junit.Assume assumptions} you can dynamically skip tests.
* Assumptions are also supported by the @Parameters
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.
*
* @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"); * } ** @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 @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 @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;
}
/**
* 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 {
}
/**
* 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 {
}
/**
* 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);
}
private void validateBeforeParamAndAfterParamMethods(Integer parameterCount)
throws InvalidTestClassError {
List