DataProviders in TestNG ( @DataProvider annotation )

TestNG provides us with many features to help us ease our automation testing. One such feature is the use of data providers in TestNG.

If you want to run a single test multiple times with different test data, you would have to duplicate your test case multiple times and pass different data to it. This will increase the complexity of maintaining different test cases.

	@Test
	public void test1() {
		// with first test data
		// do something
	}

	@Test
	public void test2() {
		// with second test data
		// do something
	}

	@Test
	public void test3() {
		// with third test data
		// do something
	}

But using data providers, we can run a single case multiple times with different data sets. This will make our cases look cleaner and easier to maintain.

This post will discuss the below topics –

Let’s look at each of them one by one.

What are data providers?

Data Providers In TestNG is a way of passing the parameters into a test method or test case. Data providers allow us to pass complex parameters or complex objects ( objects read from a property file or a database, etc… ).

We can use the data provider to provide the values we need for our test. A Data Provider is a method in our class annotated with the @DataProvider annotation and returns an array of array of objects, or we can say a 2D array.

Syntax of Data Provider and what it returns
Syntax of Data Provider
@DataProvider(name = "dataprovider_name")
public Object[][] dataProviderMethod() {
		return new Object[][] { { } };
}
  • As we said earlier, a data provider is a method annotated with @DataProvider annotation.
  • @DataProvider annotation has a name attribute.
  • The value of the name attribute will become the name of the data provider.
  • We have kept “dataprovider_name” as the name of our data provider. You can keep any name you want.
  • If we don’t specify the name attribute, then the method’s name will automatically become the name of the data provider.
@DataProvider
public Object[][] dataProviderMethod() {
		return new Object[][] { {  } };
}

Here, the name of the data provider would be “dataProviderMethod” because we didn’t specify the name attribute.

What can a @DataProvider annotated method return?

It can have two return types –

  • One is an array of array of objects or a 2D array ( Object[][] ). This is the most used return type while making data providers.
  • Another one is Iterator<Object[]>.

This post will discuss the 2D array only.

In a 2D array, the number of rows depicts how many times a test case should run, and the number of columns depicts different test data that a single case would run on.

Let’s make a data provider method that returns a 2D array with some values.

	@DataProvider(name = "dataprovider_name")
	public Object[][] dataProviderMethod() {
		return new Object[][] { 
								{ "first", 2, 3 },
								{ "second", 5, 6 }, 
								{ "third", 8, 9 }, 
								{ "fourth", 11, 12 } 
							};
	}

The test case consuming the above data provider would be executed 4 times as denoted by the number of rows. A single row contains the test data to be passed as the parameters of the test case.

2D array representing the test data
Test case using the Data Provider

Till now, we made a data provider but haven’t used it. So, we will make a test method using the above data provider.

How to use a data provider in the test case?
  • All the test methods would be annotated with the @Test annotation.
  • @Test annotation has one attribute named “dataProvider”.
  • We can provide the name of the data provider we want to use in that attribute.
  • And the data present in the 2D array columns will become our test case’s arguments.
A test case using the data provider

The whole code would be something like this –

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class CodekruTest {

	@DataProvider(name = "dataprovider_name")
	public Object[][] dataProviderMethod() {
		return new Object[][] { 
								{ "first", 2, 3 },
								{ "second", 5, 6 }, 
								{ "third", 8, 9 }, 
								{ "fourth", 11, 12 } 
							};
	}
	
	@Test(dataProvider = "dataprovider_name")
	public void test(String str, int a, int b) {
		System.out.println("str = " + str + ", a = " + a + ", b = " + b);
	}
}

The test() method would now be executed 4 times with the data mentioned in each row. Below is the output after running the above class.

str = first, a = 2, b = 3
str = second, a = 5, b = 6
str = third, a = 8, b = 9
str = fourth, a = 11, b = 12
PASSED: test("fourth", 11, 12)
PASSED: test("second", 5, 6)
PASSED: test("third", 8, 9)
PASSED: test("first", 2, 3)
Data Provider in another class

In our previous example, the test case was in the same class as that of the data provider, but what if that wasn’t the case? What if the data provider is in another class?

Below is our project structure

Demo
  |_ _ CodekruTest
  |_ _ DataProviderClass

Demo is the package’s name, whereas CodekruTest contains the test cases, and DataProviderClass contains the data providers.

If a data provider is in another class, it should satisfy one of the below conditions to be used in another class’s test case.

  • @DataProvider annotated method should be static.
  • Or, the class having a data provider should have a no-arg constructor.

If a method doesn’t satisfy any of the above conditions, it cannot be used as a data provider in the test cases of other classes.

Below is our DataProviderClass, which has a static method annotated with @DataProvider annotation.

public class DataProviderClass {
	
	@DataProvider(name = "dataprovider_name")
	public static Object[][] dataProviderMethod() {
		return new Object[][] { 
								{ "first", 2, 3 },
								{ "second", 5, 6 }, 
								{ "third", 8, 9 }, 
								{ "fourth", 11, 12 } 
							};
	}
	
}

The test cases using the above data provider would now have to mention the data provider class with the data provider name, as shown below.

@Test(dataProvider = "dataprovider_name", dataProviderClass = Demo.DataProviderClass.class)
public void test(String str, int a, int b) {
}

The “dataProviderClass” attribute takes the class name where the actual data provider method is present.

The class name would normally be in the syntax packageName1.packageName2……ClassName.class. In our case, the class was present under the Demo package, so we had written Demo.DataProviderClass.class.

Here are both of our classes side by side.

public class CodekruTest {

@Test(dataProvider = "dataprovider_name", dataProviderClass = Demo.DataProviderClass.class)
	public void test(String str, int a, int b) {
		System.out.println("str = " + str + ", a = " + a + ", b = " + b);
	}
}
public class DataProviderClass {
	
	@DataProvider(name = "dataprovider_name")
	public static Object[][] dataProviderMethod() {
		return new Object[][] { 
								{ "first", 2, 3 },
								{ "second", 5, 6 }, 
								{ "third", 8, 9 }, 
								{ "fourth", 11, 12 } 
							};
	}
	
}

We will get the below results after executing the test case of the CodekruTest class.

str = first, a = 2, b = 3
str = second, a = 5, b = 6
str = third, a = 8, b = 9
str = fourth, a = 11, b = 12
PASSED: test("fourth", 11, 12)
PASSED: test("second", 5, 6)
PASSED: test("third", 8, 9)
PASSED: test("first", 2, 3)

We got the same results as earlier.

Passing method as a parameter in @DataProvider annotated method

We haven’t passed any parameter in the @DataProvider annotated method till now. We can pass a method instance containing the method information, like its name, and access it in the @DataProvider annotated method.

@DataProvider(name = "dataprovider_name")
public static Object[][] dataProviderMethod(Method method){
}

Method class belongs to the java.lang.reflect package.

public class CodekruTest {

	@DataProvider(name = "dataprovider_name")
	public Object[][] dataProviderMethod(Method method) {
		System.out.println("Method name: "+method.getName());
		return new Object[][] { 
								{ "first", 2, 3 },
								{ "second", 5, 6 }, 
								{ "third", 8, 9 }, 
								{ "fourth", 11, 12 } 
							};
	}
	
	@Test(dataProvider = "dataprovider_name")
	public void test1(String str, int a, int b) {
		System.out.println("str = " + str + ", a = " + a + ", b = " + b);
	}
	
	@Test(dataProvider = "dataprovider_name")
	public void test2(String str, int a, int b) {
		System.out.println("str = " + str + ", a = " + a + ", b = " + b);
	}
}

Output after executing the above method –

Method name: test1
str = first, a = 2, b = 3
str = second, a = 5, b = 6
str = third, a = 8, b = 9
str = fourth, a = 11, b = 12
Method name: test2
str = first, a = 2, b = 3
str = second, a = 5, b = 6
str = third, a = 8, b = 9
str = fourth, a = 11, b = 12

We can see that the method name is printed here. There are a lot of helpful methods that the Method class provides, and you can use them based on your needs.

Parallel Execution with Data Provider

We can execute a single test case multiple times on different data sets using the data provider. By default, the data provider will run each case in series, which means a test case execution won’t start until the last one isn’t finished.

But if we are talking about multiple cases, executing all cases might take time. So to fasten up the execution, we can run the data provider in parallel such that each row of the 2D array will run in parallel.

Syntax for Parallel Execution with Data Provider

We would have to use parallel attribute of @DataProvider annotation with the value “true“.

@DataProvider(name = "dataprovider_name", parallel = true)
public Object[][] dataProviderMethod() {
   return new Object[][] {{}};
}

This will run the cases in parallel. By default, it can run 10 cases in parallel.

If we want to override the default behavior, we must define that in the testng.xml file with “data-provider-thread-count” attribute.

Below is the XML file where we have kept the “data-provider-thread-count” value as 2, which will run only 2 cases In parallel.

<suite name="codekru"  data-provider-thread-count = "2">
	<test name="codekruTest">
		<classes>
			<class name="Demo.CodekruTest" />
		</classes>
	</test>
</suite>	

Reference – https://testng.org/doc/

We hope that you have liked this article. If you have any doubts or concerns, please write us in the comments or mail us at admin@codekru.com.

Related Article
Liked the article? Share this on

Leave a Comment

Your email address will not be published. Required fields are marked *