Imagine testing a login functionality where you need to validate multiple username and password combinations. Writing separate test methods for each combination is not only tedious but also prone to redundancy and errors. This is where TestNG @DataProvider comes to the rescue, enabling you to supply test data dynamically and execute the same test logic with various inputs. This approach enhances code reusability, simplifies maintenance, and improves overall test coverage.
Note: We have also attached a video at the end of this article. In case you are more comfortable with the video version, then please feel free to have a look
In this article, we will explore the concept of Data Providers in TestNG and how to create and use them effectively. We’ll cover both basic and advanced use cases, ensuring you’re equipped to leverage this feature to its fullest potential in your automation frameworks.
- What are data providers?
- Syntax of Data Provider and What It Returns
- How to use data provider with a test case
- Data Provider in another class
- Parallel Execution with Data Provider
- Passing method as a parameter in @DataProvider annotated method
Let’s discuss the above points one by one.
What are data providers?
A Data Provider in TestNG is a mechanism that allows us to supply multiple sets of data to a single test method. It is defined using the @DataProvider
annotation and it enables us to implement data-driven testing by dynamically feeding input values to your test cases.
With a Data Provider, we can define a method that returns an array of data sets (either as an Object array or a two-dimensional array of Objects). Each data set is then used as input to the test method during its execution. This makes it possible to execute the same test logic with varying data inputs, improving the test coverage and efficiency of the testing process.
Syntax of Data Provider and What It Returns
Syntax of Data Provider
@DataProvider(name = "dataprovider_name")
public Object[][] dataProviderMethod() {
return new Object[][] { { } };
}
Key Points:
- Annotation: The method must be annotated with @DataProvider.
- Name Attribute: The name attribute specifies the identifier for the Data Provider. If name attribute is not provided, then the method name will be used as the data provider’s name.
@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 return either of two types –
- One is an array of array of objects or a 2D array ( Object[][] ). This is one of the most used return types for a data provider and will be covered in this post.
- Another one is Iterator<Object[]>.
In the 2D array,
- the number of rows depicts how many times a test case would run.
- and the number of columns depicts the actual values/test data that a single case would run on.
Example of a data provider
Let’s create 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 }
};
}
- As the number of rows of the returned array is 4, so the test case using the data provider will be executed 4 times
- The columns in each inner array correspond to the parameters required by the test method. (If this is still unclear, don’t worry; we will delve into it further in the next section.)

How to use data provider with a test case
We have created a data provider but haven’t used it yet. So, let’s create a test case that will use the data provider.
How to use a data provider in the test case?
- @Test annotation provides one attribute named “dataProvider.”
- The name of the dataProvider will become the value of the “dataProvider” attribute.
@Test(dataProvider = "dataprovider_name")
public void test(){
}
- And the data present in the 2D array columns will become our test case’s arguments.

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 will now run four times, corresponding to the number of rows in the 2D array, using the parameters specified in each row. Below is the output generated after executing 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 written in an another class?
Consider the below project structure –
Demo
|_ _ CodekruTest
|_ _ DataProviderClass
Demo is the package’s name, whereas CodekruTest contains the test cases, and DataProviderClass has the data providers.
If a data provider is defined in a different class, it must meet one of the following conditions to be used by the test cases written in another class.
- @DataProvider annotated method should be static.
- Or, the class having a data provider should have a no-arg constructor.
Note: 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 now have to mention the data provider’s name and the DataProvider class where the method was actually written.
@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 syntax of writing the class name would be packageName1.packageName2……ClassName.class. In our case, the class was present under the Demo package, so we had written Demo.DataProviderClass.class as the value of “dataProviderClass” attribute.
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 }
};
}
}
Output after running 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)
Here, we can observe that the output remains the same, with the only difference being that the dataProvider was located in a different class.
Parallel Execution with Data Provider
We can run a single test case several times across different data sets using the data provider. But by default, the data provider executes each case sequentially, which means that a new test case doesn’t start until the previous one is completed.
However, running each test sequentially might take up a significant amount of time to finish the execution. So, to reduce execution time, we can run the test cases in parallel, allowing each row of the data provider to execute simultaneously.
Syntax for Parallel Execution with Data Provider
We would have to use the “parallel” attribute of @DataProvider annotation and set its value to “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.
To override the default behavior, we need to specify it in the testng.xml file using the “data-provider-thread-count” attribute.
Below is the XML file where we have kept the “data-provider-thread-count
” value as 2, so it 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>
Passing method as a parameter to @DataProvider annotated method
We haven’t passed any parameter in the @DataProvider annotated method till now. It was like this –
@DataProvider(name = "dataprovider_name")
public static Object[][] dataProviderMethod() {
return new Object[][] {
{ "first", 2, 3 },
{ "second", 5, 6 },
{ "third", 8, 9 },
{ "fourth", 11, 12 }
};
}
But optionally, we can also pass the Method object as a parameter in the dataProvider annotated method, as shown below.
@DataProvider(name = "dataprovider_name")
public static Object[][] dataProviderMethod(Method method){
}
Method class belongs to the java.lang.reflect package. This Method object contains information like the method’s name, the class it belongs too, etc. which can then be utilized in the dataprovider annotated method.
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’s name is printed in the console. The Method class provides a lot of helpful methods, and we can use them based on our needs.
Video Tutorial
Reference – https://testng.org/doc/
We hope that you have liked this article. If you have any doubts or concerns, please write to us in the comments or email us at admin@codekru.com.