JUnit Theories: First Thoughts
One of my favourite additions to JUnit 4.4 was the @Theory annotation which allows us to write parameterised tests rather than having to recreate the same test multiple times with different data values or creating one test and iterating through our own collection of data values.
Previously, as far as I'm aware, it was only possible to parameterise tests by using the TestNG library which has some nice ideas around grouping tests but had horrible reporting the last time I used it.
To create parameterisable tests using Theories we need to write some code like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import org.junit.Test; import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; @RunWith(Theories.class) public class SomeTest { @Theory public void testTheNewTheoriesStuff(int value) { // test which involves int value } public static @DataPoints int[] values = {1,2,3,4,5}; } |
The 'testTheNewTheoriesStuff' Theory is then executed with each of the values defined in the values array decorated with the @DataPoints annotation.
The error message reported for a failure is reasonably good and makes it quite easy to figure out which one of the data points causes the problem.
An example error message for an assertion which failed inside a theory might look like this:
org.junit.experimental.theories.internal.ParameterizedAssertionError: testTheNewTheoriesStuff(values[1])
It's 0 indexed so this error message tells us that there was an error when running the theory with the 2nd data point, therefore allowing us to go and work out why that's the case and fix it.
This approach is actually particularly useful for testing the scope in which classes we pull from a dependency injection container are available from in our application.
Another potential use for this would be to test the edge cases of our classes – perhaps this would work best if we can randomise the data it uses.
This seems to be more the approach Microsoft are taking with the the Pex framework, a similar idea in the .NET space.
Thanks.
I wrote a test program using @RunWith(Theories.class) and had few static public variables that act has @Datapoint. I had some parameterized methods and used @Test (instead of @Theory) annotation.
@RunWith(Theories.class)
public class TestCalculatorUsingDataPoints {
Calculator calculator = null;
@DataPoint
public static int first_number = 10;
@DataPoint
public static int second_number = 20;
@Before
public void setUp() {
calculator = new Calculator();
}
@After
public void tearDown() {
calculator = null;
}
@Test
public void add(int number1, int number2) {
int actualResult = calculator.add(number1, number2);
//assertEquals(expectedResult, actualResult);
int expectedResult = number1 + number2;
System.out.println("add " + number1 + " " + number2);
assertThat(actualResult, is(expectedResult));
}
}
JUnit did not throw any error that @Theory should be used. JUnit recognized the @Test methods and call the same method with each datapoint.
Result:
add 10 10
add 10 20
add 20 10
add 20 20
Isn't weird?
Sai
13 Jan 09 at 10:23 pm
Theories are my favourite feature too. I like it because it makes it possible to define tests for common properties of classes.
http://blog.bader-jene.de/?p=30
lexi
11 Feb 09 at 4:26 pm
Hi lexi ,
Can you please let us know how your test method are running without error.
I have used @RunWith(Theories.class) but my test methods are expecting @Theory , if i will annotate with @Test then i am getting errors that test methods should not have any arguments
sudhanshu
13 Oct 09 at 1:11 pm
Sorry this is not meant for lexi , this is for Sai
sudhanshu
13 Oct 09 at 1:12 pm
Hi,
About the tests with the Theories, I can't use it to do what you are saying:
The 'testTheNewTheoriesStuff' Theory is then executed with each of the values defined in the values array decorated with the @DataPoints annotation.
When I use an array of Objects, the @Test/@Theory will expect an array as parameter in the method….So if I have a array of int as DataPoints, I must use an array of int as parameter in the test….. :/
Di
7 Nov 09 at 2:54 am