Thursday, October 25, 2007

TestNG's @DataProvider versus JUnit 4's Parameterized

In "Test Driven - Practical TDD and Acceptance TDD for Java Developers", Koskela used JUnit 4 as unit testing framework but I want to use TestNG so I need to "translate" from JUnit 4 to TestNG. I think it's a good opportunity to know about both of these unit testing frameworks.

In chapter 4, Koskela described about test patterns and one of them is Parameterized Test. To apply this pattern, we need to follow the rule to write the test class as below:

+ The test class must be annotated by @RunWith(Parameterized.class),
+ Supply a static method, is annotated by @Parameters and returns a Collection<Object[]> or its subclass,
+ And we need to write a constructor which has parameters for expected value and input values.

Example:
@RunWith(Parameterized.class)
public class ParameterizedTest {
// Provide parameterized data
@Parameters
public static Collection<Object[]> parameters() {
Object[][] data = new Object[][] {
{ 0, 0, 0 }, { 1, 1, 0 },
{ 2, 1, 1 }, { 3, 2, 1 },
{ 4, 3, 1 }, { 5, 5, 0 },
{ 6, 8, -2 } };
return Arrays.asList(data);
}

public int expected, input1, input2;

// Data is bound through constructor
public ParameterizedTest(int expected, int input1,
int input2) {
this.expected = expected;
this.input1 = input1;
this.input2 = input2;
}

//Test method invoked once for each data set
@Test
public void executeParameterizedTest() throws Exception {
assertEquals(expected, new Calculator().add(input1,
input2));
}
}

It looks very complicated, huh? Now, I will "convert" it to TestNG.

After awhile, I found that TestNG's @DataProvider can do the same thing. And here is the rule to write test class with TestNG following Parameterized Test pattern:

+ Supply a method (don't need to be static) is annotated by @DataProvider(name = "dataProviderName") and returns Object[][],
+ Test method is annotated by @Test(dataProvider = "dataProviderName") and has parameters as same as the Object[]'s elements in data is supplied from data provider

The above source code after was "converted":
public class ParameterizedTest {
@DataProvider(name = "data")
public Object[][] parameters() {
return new Object[][] {
{ 0, 0, 0 }, { 1, 1, 0 },
{ 2, 1, 1 }, { 3, 2, 1 },
{ 4, 3, 1 }, { 5, 5, 0 },
{ 6, 8, -2 } };
}

@Test(dataProvider = "data")
public void executeParameterizedTest(int expected,
int input1,
int input2)
throws Exception {
assert (expected == new Calculator().add(input1,
input2));
}
}

The test class is written with TestNG is much simpler also you can have many data providers as you want. And what will happen if you want to test the Calculator for substraction? If you use JUnit 4 as unit testing framework, seems you need to write another test class.

1 comment:

Andreas Schmid said...

I have also implemented a JUnit plugin to provide better parameterized tests to JUnit (similar to TestNG). As the internal structure of TestNG is a bit different from JUnit, it’s not completely equal but IMHO it provides the most important features for parameterized testing.

See https://github.com/TNG/junit-dataprovider

Feedback welcome :-)