Introduction to JUnit Theories
A normal test captures the intended behavior in one particular scenario, given an input it expects a certain output. A theory captures some aspect of the intended behavior in possibly infinite numbers of potential scenarios. This means whatever a theory asserts is expected to be true for all data sets. Theories are often used for finding bugs in boundary-value cases or mathematical theories. Theories are functionally similar to parameterized tests, but are expressively richer.
Creating a Theory
The class should be annotated with @RunWith(Theories.class)
and have:
- A data method that generates and returns test data
- By annotating a static member variable with
@DataPoint
- By annotating a static member variable with
@DataPoints
- By annotating a static member variable with
- A theory by annotating a test method with the
@Theory
annotation
Theories come up with many annotations and a class runner. Let’s examine the important annotations and classes in theory:
@Theory
same like@Test
, this annotation identifies a theory test.@DataPoint
annotation identifies a single set of test data. This annotation is similar to@Parameter
. It can be annotated by either a static variable or a method.@DataPoints
annotation identifies multiple sets of test data. This annotation is similar to@Parameters
and is generally used for an array. It can be annotated by either a static variable or a method.@ParametersSuppliedBy
annotation provides the parameters to the test cases.Theories
is a JUnit runner for running theory test classes.ParameterSupplier
is able to provide parameters that we can supply to the test case.
Passing Data Via @DataPoint
In contrast to a normal test, theories can have arguments. The data that is passed to these arguments come from a static member variable annotated by either @DataPoint
or @DataPoints
. When multiple @DataPoint
annotations are defined in a test, the theories apply to all possible type complient combinations of data points for the test arguments.
package com.memorynotfound.test;
import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
@RunWith(Theories.class)
public class TestTheoriesDataPoint {
@DataPoint
public static String dolly = "dolly";
@DataPoint
public static String parton = "parton";
@Theory
public void test(String first, String last){
System.out.println("testing first: " + first + " with last: " + last);
}
}
The previous code generates the following output. 2 x 2 combinations equals to 4 executions.
testing first: dolly with last: dolly
testing first: dolly with last: parton
testing first: parton with last: dolly
testing first: parton with last: parton
Passing Data Via @DataPoints
The @DataPoints
annotation is used to provide a set of data. It will execute the theory with 9 (3 ^ 2) possible combinations. The @DataPoints
annotations can be used on static member variables or methods.
package com.memorynotfound.test;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertTrue;
@RunWith(Theories.class)
public class TestTheoriesDataPoints {
@DataPoints
public static int[] bounds = {Integer.MIN_VALUE, 0, Integer.MAX_VALUE};
@Theory
public void addition_is_commutative_test(int a, int b){
System.out.println("a + b = " + (a + b) + " == b + a = " + (b + a));
assertTrue(a + b == b + a);
}
}
The previous code generates the following output.
a + b = 0 == b + a = 0
a + b = -2147483648 == b + a = -2147483648
a + b = -1 == b + a = -1
a + b = -2147483648 == b + a = -2147483648
a + b = 0 == b + a = 0
a + b = 2147483647 == b + a = 2147483647
a + b = -1 == b + a = -1
a + b = 2147483647 == b + a = 2147483647
a + b = -2 == b + a = -2