- * 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:
- *
- *
- * 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:
- *
- *
- * 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.
- *
- *
- * @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
- */
- @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;
- }
-
- private class TestClassRunnerForParameters extends BlockJUnit4ClassRunner {
- private final Object[] fParameters;
-
- private final String fName;
-
- TestClassRunnerForParameters(Class> type, Object[] parameters,
- String name) throws InitializationError {
- super(type);
- fParameters = parameters;
- fName = name;
- }
-
- @Override
- public Object createTest() throws Exception {
- if (fieldsAreAnnotated()) {
- return createTestUsingFieldInjection();
- } else {
- return createTestUsingConstructorInjection();
- }
- }
-
- private Object createTestUsingConstructorInjection() throws Exception {
- return getTestClass().getOnlyConstructor().newInstance(fParameters);
- }
-
- private Object createTestUsingFieldInjection() throws Exception {
- List annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
- if (annotatedFieldsByParameter.size() != fParameters.length) {
- throw new Exception("Wrong number of parameters and @Parameter fields." +
- " @Parameter fields counted: " + annotatedFieldsByParameter.size() + ", available parameters: " + fParameters.length + ".");
- }
- Object testClassInstance = getTestClass().getJavaClass().newInstance();
- for (FrameworkField each : annotatedFieldsByParameter) {
- Field field = each.getField();
- Parameter annotation = field.getAnnotation(Parameter.class);
- int index = annotation.value();
- try {
- field.set(testClassInstance, fParameters[index]);
- } catch (IllegalArgumentException iare) {
- throw new Exception(getTestClass().getName() + ": Trying to set " + field.getName() +
- " with the value " + fParameters[index] +
- " that is not the right type (" + fParameters[index].getClass().getSimpleName() + " instead of " +
- field.getType().getSimpleName() + ").", iare);
- }
- }
- return testClassInstance;
- }
-
- @Override
- protected String getName() {
- return fName;
- }
-
- @Override
- protected String testName(FrameworkMethod method) {
- return method.getName() + getName();
- }
-
- @Override
- protected void validateConstructor(List errors) {
- validateOnlyOneConstructor(errors);
- if (fieldsAreAnnotated()) {
- validateZeroArgConstructor(errors);
- }
- }
-
- @Override
- protected void validateFields(List errors) {
- super.validateFields(errors);
- if (fieldsAreAnnotated()) {
- List annotatedFieldsByParameter = getAnnotatedFieldsByParameter();
- int[] usedIndices = new int[annotatedFieldsByParameter.size()];
- for (FrameworkField each : annotatedFieldsByParameter) {
- int index = each.getField().getAnnotation(Parameter.class).value();
- if (index < 0 || index > annotatedFieldsByParameter.size() - 1) {
- errors.add(
- new Exception("Invalid @Parameter value: " + index + ". @Parameter fields counted: " +
- annotatedFieldsByParameter.size() + ". Please use an index between 0 and " +
- (annotatedFieldsByParameter.size() - 1) + ".")
- );
- } else {
- usedIndices[index]++;
- }
- }
- for (int index = 0; index < usedIndices.length; index++) {
- int numberOfUse = usedIndices[index];
- if (numberOfUse == 0) {
- errors.add(new Exception("@Parameter(" + index + ") is never used."));
- } else if (numberOfUse > 1) {
- errors.add(new Exception("@Parameter(" + index + ") is used more than once (" + numberOfUse + ")."));
- }
- }
- }
- }
-
- @Override
- protected Statement classBlock(RunNotifier notifier) {
- return childrenInvoker(notifier);
- }
-
- @Override
- protected Annotation[] getRunnerAnnotations() {
- return new Annotation[0];
- }
- }
-
- private static final List NO_RUNNERS = Collections
- .emptyList();
-
- private final ArrayList runners = new ArrayList();
-
- /**
- * Only called reflectively. Do not use programmatically.
- */
- public Parameterized(Class> klass) throws Throwable {
- super(klass, NO_RUNNERS);
- Parameters parameters = getParametersMethod().getAnnotation(
- Parameters.class);
- createRunnersForParameters(allParameters(), parameters.name());
- }
-
- @Override
- protected List getChildren() {
- return runners;
- }
-
- @SuppressWarnings("unchecked")
- private Iterable