Saturday, August 22, 2009

A few notes on TestNG data providers

TestNG has a notion of a Data Provider which means a method annotated with @DataProvider annotation and returning result of type Object[][]. This methods can be used to provide sets of arguments to test methods (you can see official explanation here). Such methods are proved to be very useful for me as I'm doing TDD a lot and often :). I want to mention just two of possible usages which became integral part of my testing style.

The first one is a God send if you like to do some basic (or not very basic) argument checks on a method start (and also want to test that it really works). As example suppose we have methodA for some class under test which takes 3 String arguments. Before I started to use Data Providers I used to have lots of such tests like the following:

@Test(expectedException = IllegalArgumentException.class)
public void methodAFailNullArg1() {
testInstance.methodA(null, "arg2", "arg3");
}

@Test(expectedException = IllegalArgumentException.class)
public void methodAFailNullArg2() {
testInstance.methodA("arg1", null, "arg3");
}

@Test(expectedException = IllegalArgumentException.class)
public void methodAFailNullArg3() {
testInstance.methodA("arg1", "arg2" , null);
}
This is very annoying but if you're doing TDD you have no choice but to write this kind of tests along with others (at least I think I don't have :) )

No look how this would look like with Data Providers:

@DataProvider
private Object[][] methodABadArgsProvider() {
return new Object[][] {
{null, "arg2", "arg3"},
{"arg1", null, "arg3"},
{"arg1", "arg2", null}
}
}

@Test(expectedException = IllegalArgumentException.class, dataProvider = "methodABadArgsProvider")
public void methodABadArgs(String arg1, String arg2, String arg3) {
testInstance.methodA(arg1, arg2, arg3);
}
I'm using the latter approach starting with 2 argument sets. The main advantage is that concern of testing bad arguments for methodA here addressed in one test instead of three, so you always have at least less semantic clutter. And starting with 3-4 argument sets you have also less syntactic clutter which is even better!

The second use case is to use Data Providers to provide test instances. This can be very useful when you're going to test interface contract or some common abstract superclass functionality while using several different realizations.
For example you can have SetTest class intended to test some common for all sets functionality (for example that they can not contain more than one same element). Then it can look like this:

public class SetTest {

@DataProvider
private Object[][] instanceProvider() {
return new Object[][] {
{new HashSet()}, {new TreeSet()}
}
}

@Test(dataProvider = "instanceProvider")
public void onlyOneSameElement(Set testInstance) {
testInstance.add("string");
testInstance.add("string");
assertEquals(testInstance.size(), 1);
}
Surely the use of Data Providers aren't limited by mentioned use cases.
Also currently I don't know whether JUnit has a similar feature (the last version I worked with about a year ago was 4.3 and I can not remember there anything like this)

No comments:

Post a Comment