Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to attach a DataPoint with a Theory?

@DataPoints public static final Integer[] input1={1,2};
@Theory
@Test
public void test1(int input1){

}

@DataPoints public static final Integer[] input2={3,4};
@Theory
@Test
public void test2(int input2 ){

}

I want that test1 runs with data set input1 - {1,2} and test2 runs with input2 - {3,4}. But currently each test runs with both the data sets {1,2,3,4}. How to bind specific @DataPoints to specific @Theorys

like image 699
Sanjeev Kumar Dangi Avatar asked Sep 26 '11 11:09

Sanjeev Kumar Dangi


4 Answers

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
like image 170
Gábor Lipták Avatar answered Oct 18 '22 14:10

Gábor Lipták


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.

like image 22
Matthew Farwell Avatar answered Oct 18 '22 14:10

Matthew Farwell


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);
        }
    }
like image 29
Marcin Avatar answered Oct 18 '22 16:10

Marcin


Some of the references I have seen talking about using tests for specific values and theories for verifying behavior. As an example, if you have a class that has methods to add and subtract from an attribute, a test would verify correctness of the result (e.g., 1+3 returns 4) whereas a theory might verify that, for the datapoint values (x1, y1), (x2, y2), x+y-y always equals x, x-y+y always equals x, x*y/y always equals x, etc. This way, the results of theories are not coupled as tightly with the data. With theories, you also can filter out cases such as y == 0; they don't count as failure. Bottom line: you can use both. A good paper is: http://web.archive.org/web/20110608210825/http://shareandenjoy.saff.net/tdd-specifications.pdf

like image 42
Jack McRoberts Avatar answered Oct 18 '22 15:10

Jack McRoberts