Data providers is a very common need when you are building tests that are data driven in nature.
TestNG provides for two types of data providers.
Both the above flavors of data providers can be used in your TestNG driven tests to satisfy the following scenarios.
@Test
annotated test method “n” times.@Test
annotated test methods “n” times wherein the test methods leverage the data provided to it for every iteration.When you are working with a data set that is relatively small (for e.g., you are having a spreadsheet/text file/XML file/JSON file/Yaml file that has just a few 100 records) you would want to load all the data in one shot and then have TestNG work with the loaded data. This way of facilitating data driven tests is called as a Greedy Data Provider.
A simple format of a data provider of this format would look like below
public class GreedyDataProviderExample {
@DataProvider(name = "data-source")
public Object[][] allDataInOneShot() {
return new Object[][] { { "Java" }, { "TestNG" }, { "JUnit" } };
}
@Test(dataProvider = "data-source")
public void myTestMethod(String info) {
Reporter.log("Data provided was :" + info, true);
}
}
For a basic understanding of the syntax and semantics of Data providers you should always refer to the TestNG documentation
But here’s a quick recap of the most important things that you need to remember.
@DataProvider
. You can choose to give it a name using the “name” attribute. If you don’t provide a name, then TestNG will consider the method name to be the data provider name as well.dataProvider
attribute of the @Test
annotation to inform TestNG that the test method is not a regular method and it needs to be executed 1 or more times. The parameter that the Test method is expecting would be provided to it by the data provider method.In the case of a Greedy data provider, following is how TestNG goes about executing your test.
@Test
annotated methods (which are considered to be test methods)@Test
annotated test method.@DataProvider
annotated method and creates the 2 dimensional array of Object and stores it separately.As seen above, since TestNG loads your entire test data in one shot at the beginning itself, this mechanism of working with data providers is called greedy data providers.
Now suppose you are working with a very huge data set, or lets assume that your test data flows to you via a stream (such as network call ), then you would realize that this mechanism doesn’t scale very well. This is where Lazy data providers are handy.
A Lazy Data provider in TestNG is basically a data provider, that loads the data required for a given iteration one set at a time. So lets say you have a file that has 1 million records of employees, which you want to iterate for a test method, then TestNG would ensure that it doesn’t load all the 1 million records in one shot, but only load 1 record at a time for all the 1 million iterations.
A simple format of a data provider of this format would look like below:
public class LazyDataProviderExample {
@Test(dataProvider = "data-source")
public void myTestMethod(String info) {
Reporter.log("Data provided was :" + info, true);
}
@DataProvider(name = "data-source")
public Iterator<Object[]> dataOneByOne() {
return new MyData();
}
private static class MyData implements Iterator<Object[]> {
private String[] data = new String[] { "Java", "TestNG", "JUnit" };
private int index = 0;
@Override
public boolean hasNext() {
return (index <= (data.length - 1));
}
@Override
public Object[] next() {
return new Object[] { data[index++] };
}
@Override
public void remove() {
throw new UnsupportedOperationException("Removal of items is not supported");
}
}
}
Some important things to remember when working with Lazy data providers :
What we have seen so far is how do you bind a data provider to a single @Test
annotated test method. So what if we wanted two or more @Test
annotated test methods to work with a same piece of test data in a data driven fashion ?
This is where we would leverage Factories and data driven test approach in TestNG.
There are two styles in which you do this :
@Factory
annotation and tie it to a data provider (or)@Factory
annotation ] and tie it to a data provider.So lets look at both the variants.
Here’s a sample wherein the constructor is annotated with a @Factory
annotation and is tied to a data provider, so that we can iterator over multiple sets of data.
public class ConstructorPowererdDataDrivenTest {
private int age;
private String name;
@Factory(dataProvider = "get-data")
public ConstructorPowererdDataDrivenTest(String name, int age) {
this.name = name;
this.age = age;
}
@Test
public void testValidName() {
Assert.assertTrue(name != null && !name.trim().isEmpty());
}
@Test
public void testValidAge() {
Assert.assertTrue(age > 0);
}
@DataProvider(name = "get-data")
public static Object[][] getData() {
return new Object[][] { { "John", 10 }, { "Peter", 20 } };
}
}
Here’s another sample wherein a static method produces the instances.
public class FactoryMethodPoweredDataDrivenTest {
private String name;
private int age;
public FactoryMethodPoweredDataDrivenTest(String name, int age) {
this.name = name;
this.age = age;
}
@Test
public void testValidName() {
Assert.assertTrue(name != null && !name.trim().isEmpty());
}
@Test
public void testValidAge() {
Assert.assertTrue(age > 0);
}
@Factory(dataProvider = "get-data")
public static Object[] produceTestClasses(String name, int age) {
return new Object[] { new FactoryMethodPoweredDataDrivenTest(name, age) };
}
@DataProvider(name = "get-data")
public static Object[][] getData() {
return new Object[][] { { "Tom", 10 }, { "Jerry", 20 } };
}
}
The above samples use the Greedy data provider mechanism, but you can always replace it with a lazy data provider mechanism and use the Iterator
approach.
For any queries, log an issue here.
Tags: TestNG