How to attach a DataPoint with a Theory?
With JUnit 4.12 (not sure when it was introduced) it is possible to name the DataPoints and assign them to parameters (i learned it from http://farenda.com/junit/junit-theories-with-datapoints/):
@RunWith(Theories.class)
public class TheoriesAndDataPointsTest {
@DataPoints("a values")
public static int[] aValues() {
return new int[]{1, 2};
}
@DataPoints("b values")
public static int[] bValues() {
return new int[]{3, 4};
}
@Theory
public void theoryForA(@FromDataPoints("a values") int a) {
System.out.printf("TheoryForA called with a = %d\n", a);
}
@Theory
public void theoryForB(@FromDataPoints("b values") int a) {
System.out.printf("TheoryForB called with b = %d\n", a);
}
}
Output:
TheoryForA called with a = 1
TheoryForA called with a = 2
TheoryForB called with b = 3
TheoryForB called with b = 4
In reference to Gábor Lipták's answer, named datapoints can be defined as a static fields (reference) which give us more concise code:
@RunWith(Theories.class)
public class TheoriesAndDataPointsTest {
@DataPoints("a values")
public static int[] aValues = {1, 2};
@DataPoints("b values")
public static int[] bValues = {3, 4};
@Theory
public void theoryForA(@FromDataPoints("a values") int a) {
System.out.printf("TheoryForA called with a = %d\n", a);
}
@Theory
public void theoryForB(@FromDataPoints("b values") int a) {
System.out.printf("TheoryForB called with b = %d\n", a);
}
}
DataPoints apply to the class. If you have a @Theory method which takes an int, and you have a DataPoint which is an array of ints, then it will be called with the int.
@RunWith(Theories.class)
public class TheoryTest {
@DataPoint public static int input1 = 45;
@DataPoint public static int input2 = 46;
@DataPoints public static String[] inputs = new String[] { "foobar", "barbar" };
@Theory public void testString1(String input) {
System.out.println("testString1 input=" + input);
}
@Theory public void testString2(String input) {
System.out.println("testString2 input=" + input);
}
@Theory public void test1(int input) {
System.out.println("test1 input=" + input);
}
@Theory public void test2(int input) {
System.out.println("test2 input=" + input);
}
}
This calls test1 with 45 & 46, and test2 with 45 & 46. It calls testString1 with "foobar" and "barbar" and testString2 with "foobar" and "barbar".
If you really want to use different data sets for different theories, you can wrap the data in a private class:
@RunWith(Theories.class)
public class TheoryTest {
public static class I1 { int i; public I1(int i) { this.i = i;} }
public static class I2 { int i; public I2(int i) { this.i = i;} }
@DataPoint public static I1 input1 = new I1(45);
@DataPoint public static I2 input2 = new I2(46);
@Theory
public void test1(I1 input) {
System.out.println("test1 input=" + input.i);
}
@Theory
public void test2(I2 input) {
System.out.println("test2 input=" + input.i);
}
}
This calls test1 with 45 and test2 with 46. This works, but in my opinion, it obscures the code, and it may be a better solution to just split the Test class into two classes.