Permalink
Browse files

Merge pull request #484 from jherault/feature

[Enhancement] for parameterized JUnit test, Parameter annotation - V3
  • Loading branch information...
2 parents f9ec047 + def7dc5 commit 2e53904ae22336000cdf8e12166436e18e538b87 @dsaff dsaff committed Aug 21, 2012
@@ -169,7 +169,7 @@ protected void validateInstanceMethods(List<Throwable> errors) {
errors.add(new Exception("No runnable methods"));
}
- private void validateFields(List<Throwable> errors) {
+ protected void validateFields(List<Throwable> errors) {
RULE_VALIDATOR.validate(getTestClass(), errors);
}
@@ -417,4 +417,4 @@ private long getTimeout(Test annotation) {
return 0;
return annotation.timeout();
}
-}
+}
@@ -5,13 +5,15 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.runner.Runner;
import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.model.FrameworkField;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
@@ -73,6 +75,35 @@
* 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>
+ * &#064;RunWith(Parameterized.class)
+ * public class FibonacciTest {
+ * &#064;Parameters
+ * public static Iterable&lt;Object[]&gt; data() {
+ * return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
+ * { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
+ * }
+ * &#064;Parameter(0)
+ * public int fInput;
+ *
+ * &#064;Parameter(1)
+ * public int fExpected;
+ *
+ * &#064;Test
+ * public void test() {
+ * assertEquals(fExpected, Fibonacci.compute(fInput));
+ * }
+ * }
+ * </pre>
+ *
+ * <p>
+ * Each instance of <code>FibonacciTest</code> will be constructed with the default constructor
+ * and fields annotated by <code>&#064;Parameter</code> will be initialized
+ * with the data values in the <code>&#064;Parameters</code> method.
+ * </p>
* @since 4.0
*/
public class Parameterized extends Suite {
@@ -108,6 +139,26 @@
String name() default "{index}";
}
+ /**
+ * Annotation for fields of the test class which will be initialized by the
+ * method annotated by <code>Parameters</code><br/>
+ * By using directly this annotation, the test class constructor isn't needed.<br/>
+ * 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 <code>Parameters</code>.<br/>
+ * 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;
@@ -122,8 +173,38 @@
@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<FrameworkField> 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() {
@@ -138,6 +219,38 @@ protected String testName(FrameworkMethod method) {
@Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
+ if (fieldsAreAnnotated()) {
+ validateZeroArgConstructor(errors);
+ }
+ }
+
+ @Override
+ protected void validateFields(List<Throwable> errors) {
+ super.validateFields(errors);
+ if (fieldsAreAnnotated()) {
+ List<FrameworkField> 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
@@ -224,4 +337,12 @@ private Exception parametersMethodReturnedWrongType() throws Exception {
className, methodName);
return new Exception(message);
}
+
+ private List<FrameworkField> getAnnotatedFieldsByParameter() {
+ return getTestClass().getAnnotatedFields(Parameter.class);
+ }
+
+ private boolean fieldsAreAnnotated() {
+ return !getAnnotatedFieldsByParameter().isEmpty();
+ }
}
@@ -3,11 +3,13 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.junit.experimental.results.PrintableResult.testResult;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
@@ -18,7 +20,9 @@
import org.junit.runner.Result;
import org.junit.runner.RunWith;
import org.junit.runner.Runner;
+import org.junit.runner.notification.Failure;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.model.InitializationError;
@@ -102,6 +106,120 @@ public void usesIndexAsTestName() {
assertEquals("[1]", description.getChildren().get(1).getDisplayName());
}
+ @RunWith(Parameterized.class)
+ static public class FibonacciWithParameterizedFieldTest {
+ @Parameters
+ public static Collection<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, fib(fInput));
+ }
+
+ private int fib(int x) {
+ return 0;
+ }
+ }
+
+ @Test
+ public void countWithParameterizedField() {
+ Result result= JUnitCore.runClasses(FibonacciWithParameterizedFieldTest.class);
+ assertEquals(7, result.getRunCount());
+ assertEquals(6, result.getFailureCount());
+ }
+
+ @Test
+ public void failuresNamedCorrectlyWithParameterizedField() {
+ Result result= JUnitCore.runClasses(FibonacciWithParameterizedFieldTest.class);
+ assertEquals(String
+ .format("test[1](%s)", FibonacciWithParameterizedFieldTest.class.getName()), result
+ .getFailures().get(0).getTestHeader());
+ }
+
+ @Test
+ public void countBeforeRunWithParameterizedField() throws Exception {
+ Runner runner= Request.aClass(FibonacciWithParameterizedFieldTest.class).getRunner();
+ assertEquals(7, runner.testCount());
+ }
+
+ @Test
+ public void plansNamedCorrectlyWithParameterizedField() throws Exception {
+ Runner runner= Request.aClass(FibonacciWithParameterizedFieldTest.class).getRunner();
+ Description description= runner.getDescription();
+ assertEquals("[0]", description.getChildren().get(0).getDisplayName());
+ }
+
+ @RunWith(Parameterized.class)
+ static public class BadIndexForAnnotatedFieldTest {
+ @Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { { 0 } });
+ }
+
+ @Parameter(2)
+ public int fInput;
+
+ public int fExpected;
+
+ @Test
+ public void test() {
+ assertEquals(fExpected, fib(fInput));
+ }
+
+ private int fib(int x) {
+ return 0;
+ }
+ }
+
+ @Test
+ public void failureOnInitialization() {
+ Result result = JUnitCore.runClasses(BadIndexForAnnotatedFieldTest.class);
+ assertEquals(2, result.getFailureCount());
+ List<Failure> failures = result.getFailures();
+ assertEquals("Invalid @Parameter value: 2. @Parameter fields counted: 1. Please use an index between 0 and 0.",
+ failures.get(0).getException().getMessage());
+ assertEquals("@Parameter(0) is never used.", failures.get(1).getException().getMessage());
+ }
+
+ @RunWith(Parameterized.class)
+ static public class BadNumberOfAnnotatedFieldTest {
+ @Parameters
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { { 0, 0 } });
+ }
+
+ @Parameter(0)
+ public int fInput;
+
+ public int fExpected;
+
+ @Test
+ public void test() {
+ assertEquals(fExpected, fib(fInput));
+ }
+
+ private int fib(int x) {
+ return 0;
+ }
+ }
+
+ @Test
+ public void numberOfFieldsAndParametersShouldMatch() {
+ Result result = JUnitCore.runClasses(BadNumberOfAnnotatedFieldTest.class);
+ assertEquals(1, result.getFailureCount());
+ List<Failure> failures = result.getFailures();
+ assertTrue(failures.get(0).getException().getMessage().contains("Wrong number of parameters and @Parameter fields. @Parameter fields counted: 1, available parameters: 2."));
+ }
+
private static String fLog;
@RunWith(Parameterized.class)
@@ -254,4 +372,4 @@ public void aTest() {
public void exceptionWhenPrivateConstructor() throws Throwable {
new Parameterized(PrivateConstructor.class);
}
-}
+}

0 comments on commit 2e53904

Please sign in to comment.