DataProvider in TestNG: Selenium Parameterization Example
โก Smart Summary
Parameterization in Selenium runs a single test method repeatedly against different data sets, turning one script into data-driven coverage. TestNG offers two mechanisms for this: the @Parameters annotation backed by testng.xml, and the @DataProvider annotation returning a two-dimensional object array.

As we create software, we always want it to work correctly with different sets of data. When it comes to testing that software, checking a single set of data is not enough. We need to verify that the system accepts every combination it is expected to support. For that, we need to parameterize our test scripts. This is where parameterization comes in.
Parameterization in Selenium
Parameterization in Selenium is a process to parameterize the test scripts in order to pass multiple data to the application at runtime. It is a strategy of execution which automatically runs test cases multiple times using different values. The concept achieved by parameterizing the test scripts is called Data Driven Testing.
Type of Parameterization in TestNG
To make parameterization clearer, we will go through the parameterization options in one of the most popular frameworks for Selenium WebDriver โ TestNG.
There are two ways by which we can achieve parameterization in TestNG:
- With the help of the Parameters annotation and the TestNG XML file.
- With the help of the DataProvider annotation.
The diagram above summarizes the split: parameters from testng.xml can be declared at suite or test level, while a parameter from a DataProvider can accept Method and ITestContext as its own argument. Let us study them in detail.
Parameters Annotation in TestNG
Parameters Annotation in TestNG is a method used to pass values to the test methods as arguments using an .xml file. Users may be required to pass the values to the test methods during run time. The @Parameters annotation method can be used in any method having @Test, @Before, @After or @Factory annotation.
Parameters annotation with Testng.xml
Select parameterization using annotations when you do not want to deal with complexity and the number of input combinations is small.
Let us see how this works.
Test Scenario
Step 1) Launch the browser and go to Google.com
Step 2) Enter a search keyword
Step 3) Verify the entered value is the same as the value provided by our test data
Step 4) Repeat steps 2 and 3 until all values are entered
| Test Author | SearchKey |
|---|---|
| Guru99 | India |
| Krishna | USA |
| Bhupesh | China |
Here is an example of how to do it without parameters:
package parameters; import org.testng.annotations.Test; import org.testng.AssertJUnit; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; public class NoParameterWithTestNGXML { WebDriver driver; @Test public void testNoParameter() throws InterruptedException { String author = "guru99"; String searchKey = "india"; driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); WebElement searchText = driver.findElement(By.name("q")); // Searching text in the Google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); System.out.println("Value in Google Search Box = " + searchText.getDomProperty("value") + " ::: Value given by input = " + searchKey); // Verifying the value in the Google search box AssertJUnit.assertTrue(searchText.getDomProperty("value").equalsIgnoreCase(searchKey)); } }
โ ๏ธ Selenium 4 note: Three lines in the original version of this example no longer compile or are deprecated on current Selenium releases. System.setProperty("webdriver.gecko.driver", โฆ) is unnecessary from Selenium 4.6 onward because Selenium Manager resolves the driver binary automatically. The implicitlyWait(10, TimeUnit.SECONDS) overload was removed in Selenium 4 and replaced by implicitlyWait(Duration.ofSeconds(10)). getAttribute() was deprecated in Selenium 4.27, so reading a text box uses getDomProperty("value"). Every example below applies the same three corrections.
Study the example above and imagine how complex the code becomes when this is repeated for three input combinations.
Now let us parameterize it using TestNG. To do so, you need to:
- Create an XML file which will store the parameters
- In the test, add the annotation @Parameters
Here is the complete code.
Test Level TestNG.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="TestSuite" thread-count="3"> <parameter name="author" value="Guru99" /> <parameter name="searchKey" value="India" /> <test name="testGuru"> <parameter name="searchKey" value="UK" /> <classes> <class name="parameters.ParameterWithTestNGXML"></class> </classes> </test> </suite>
ParameterWithTestNGXML.java File
package parameters; import org.testng.AssertJUnit; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; public class ParameterWithTestNGXML { WebDriver driver; @Test @Parameters({"author", "searchKey"}) public void testParameterWithXML(@Optional("Abc") String author, String searchKey) throws InterruptedException { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); WebElement searchText = driver.findElement(By.name("q")); // Searching text in the Google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); System.out.println("Value in Google Search Box = " + searchText.getDomProperty("value") + " ::: Value given by input = " + searchKey); // Verifying the value in the Google search box AssertJUnit.assertTrue(searchText.getDomProperty("value").equalsIgnoreCase(searchKey)); } }
To run the script, select the XML file and run it as a TestNG suite.
Right click on the .xml file -> Run as -> TestNG Suite (Note: Suite)
Parameters can be defined at two levels:
- Suite level โ the parameters inside the
<suite>tag of the TestNG XML file are suite level parameters. - Test level โ the parameters inside the
<test>tag of the TestNG XML file are test level parameters.
Here is the same test with suite level parameters:
NOTE: If the parameter name is the same at suite level and test level, the test level parameter takes preference over the suite level one. In that case, all classes inside that test level share the overridden parameter, and classes outside that test level continue to use the suite level parameter.
Troubleshooting
Issue # 1: A parameter value in testng.xml that cannot be typecast to the corresponding test method’s parameter throws an error.
Consider the following example:
Here, the โauthorโ attribute equals โGuru99โ, which is a String, while the corresponding test method expects an integer value, so an exception is raised.
Issue # 2: Your @Parameters do not have a corresponding value in testng.xml.
You can solve this by adding the @Optional annotation to the corresponding parameter in the test method.
Issue # 3: You want to test multiple values of the same parameter using testng.xml.
The simple answer is that this cannot be done. You can have multiple different parameters, but each parameter can only hold a single value. This prevents hardcoding values into the script and keeps the code reusable โ think of it as a config file for your script. If you need multiple values for one parameter, use a DataProvider instead.
Data Provider in TestNG
Data Provider in TestNG is a method used when a user needs to pass complex parameters. Complex parameters need to be created from Java โ complex objects, objects from property files, or objects from a database can be passed by the data provider method. The method is annotated with @DataProvider and it returns an array of objects.
Parameters using Dataprovider
The @Parameters annotation is easy, but to test with multiple sets of data we need to use a Data Provider.
To fill thousands of web forms using our testing framework, we need a different methodology which can supply a very large dataset in a single execution flow.
This data driven concept is achieved by the @DataProvider annotation in TestNG.
It has only one attribute, โnameโ. If you do not specify the name attribute, the DataProvider’s name is the same as the corresponding method name.
A data provider returns a two-dimensional Java object to the test method, and the test method is invoked M times for an MรN object array. For example, if the DataProvider returns an array of 2ร3 objects, the corresponding test case is invoked 2 times with 3 parameters each time.
Complete Example
package parameters; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class ParameterByDataprovider { WebDriver driver; @BeforeTest public void setup() { // Create the Firefox driver object driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); } /** Test case to verify the Google search box */ @Test(dataProvider = "SearchProvider") public void testMethod(String author, String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); // Search value in the Google search box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); // Verify if the value in the Google search box is correct Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } /** * @return Object[][] where the first column contains 'author' * and the second column contains 'searchKey' */ @DataProvider(name = "SearchProvider") public Object[][] getDataFromDataprovider() { return new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; } }
Invoke DataProvider from different class
By default, a DataProvider resides in the same class as the test method, or in its base class. To place it in another class, make the data provider method static and add the attribute dataProviderClass to the @Test annotation.
Code Example
TestClass ParameterDataproviderWithClassLevel.java
package parameters; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; public class ParameterDataproviderWithClassLevel { WebDriver driver; @BeforeTest public void setup() { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); } @Test(dataProvider = "SearchProvider", dataProviderClass = DataproviderClass.class) public void testMethod(String author, String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); // Search text in the Google text box searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); // Get text from the search box String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); // Verify if the search box has the correct value Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } }
DataproviderClass.java
package parameters; import org.testng.annotations.DataProvider; public class DataproviderClass { @DataProvider(name = "SearchProvider") public static Object[][] getDataFromDataprovider() { return new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; } }
Types of Parameters in Dataprovider
There are two types of parameters supported by the DataProvider method.
Method โ if the same DataProvider should behave differently for different test methods, use the Method parameter.
In the following example:
- We check if the method name is testMethodA
- If yes, return one set of values
- Otherwise return another set of values
package parameters; import java.lang.reflect.Method; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class ParameterByMethodInDataprovider { WebDriver driver; @BeforeTest public void setup() { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); } @Test(dataProvider = "SearchProvider") public void testMethodA(String author, String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); // Print author and search string System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } @Test(dataProvider = "SearchProvider") public void testMethodB(String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); // Print only the search string System.out.println("Welcome ->Unknown user Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } /** The DataProvider returns values based on the test method name */ @DataProvider(name = "SearchProvider") public Object[][] getDataFromDataprovider(Method m) { if (m.getName().equalsIgnoreCase("testMethodA")) { return new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; } else { return new Object[][] { { "Canada" }, { "Russia" }, { "Japan" } }; } } }
Here is the output:
ITestContext โ this can be used to create different parameters for test cases based on groups.
In real life, you can use ITestContext to vary parameter values based on test methods, hosts, or test configurations.
In the following code example:
- We have 2 groups, A and B
- Each test method is assigned to a group
- If the group value is A, one data set is returned
- If the group value is B, another data set is returned
package parameters; import java.time.Duration; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.testng.Assert; import org.testng.ITestContext; import org.testng.annotations.BeforeTest; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class ParameterByITestContextInDataprovider { WebDriver driver; @BeforeTest(groups = {"A", "B"}) public void setup() { driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); driver.get("https://google.com"); } @Test(dataProvider = "SearchProvider", groups = "A") public void testMethodA(String author, String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); System.out.println("Welcome ->" + author + " Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } @Test(dataProvider = "SearchProvider", groups = "B") public void testMethodB(String searchKey) throws InterruptedException { WebElement searchText = driver.findElement(By.name("q")); searchText.sendKeys(searchKey); System.out.println("Welcome ->Unknown user Your search key is->" + searchKey); Thread.sleep(3000); String testValue = searchText.getDomProperty("value"); System.out.println(testValue + "::::" + searchKey); searchText.clear(); Assert.assertTrue(testValue.equalsIgnoreCase(searchKey)); } /** The DataProvider supplies an Object array based on ITestContext */ @DataProvider(name = "SearchProvider") public Object[][] getDataFromDataprovider(ITestContext c) { Object[][] groupArray = null; for (String group : c.getIncludedGroups()) { if (group.equalsIgnoreCase("A")) { groupArray = new Object[][] { { "Guru99", "India" }, { "Krishna", "UK" }, { "Bhupesh", "USA" } }; break; } else if (group.equalsIgnoreCase("B")) { groupArray = new Object[][] { { "Canada" }, { "Russia" }, { "Japan" } }; break; } } return groupArray; } }
Note: If you run the TestNG class directly, it calls the data provider first, and the provider cannot read group information because the groups are not available yet. Calling the class through testng.xml makes the group information available through ITestContext. Use the following XML to run the test:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd"> <suite name="test-parameter"> <test name="example1"> <groups> <run> <include name="A" /> </run> </groups> <classes> <class name="parameters.ParameterByITestContextInDataprovider" /> </classes> </test> <test name="example2"> <groups> <run> <include name="B" /> </run> </groups> <classes> <class name="parameters.ParameterByITestContextInDataprovider" /> </classes> </test> </suite>
Both mechanisms are now covered, so the practical question is which one to reach for.
@Parameters vs @DataProvider: Which One to Use
The two approaches are not interchangeable. The decision comes down to how many value combinations a test needs and where those values originate.
| Criterion | @Parameters + testng.xml | @DataProvider |
|---|---|---|
| Values per parameter | Exactly one | Unlimited rows |
| Data source | Static text in the XML file | Java code, Excel, CSV, database, API |
| Supported types | Values typecast from String | Any Java object |
| Test invocations | Once per configuration | Once per data row |
| Changing data requires | Editing the XML file | Editing the source, often no recompile |
| Best suited to | Environment and configuration values such as browser, URL, credentials | Data driven testing across many input combinations |
In practice most suites use both: @Parameters carries the environment settings that stay constant across a run, and @DataProvider supplies the varying business data. When the data set outgrows a hardcoded array, the next step is reading it from a spreadsheet.
How to Read Test Data From an Excel File Using DataProvider
Hardcoded arrays stop scaling once testers who do not write Java need to maintain the data. Moving the rows into a spreadsheet separates test data from test logic, which is the practical form of data driven testing. Apache POI reads the workbook, and the DataProvider converts each row into one test invocation.
- Add the dependency. Include
org.apache.poi:poi-ooxmlin your Maven or Gradle build. Theooxmlartifact handles the .xlsx format; plainpoionly reads legacy .xls files. - Create the workbook. Put the header row in row 0 and one test case per row beneath it, with one column per test method argument.
- Read the sheet into an array. Size the Object[][] from the row and cell counts so new rows are picked up without code changes.
- Return it from the provider. The test method signature stays exactly as before.
import java.io.FileInputStream; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.testng.annotations.DataProvider; public class ExcelDataProvider { @DataProvider(name = "SearchProvider") public Object[][] readSearchData() throws Exception { FileInputStream file = new FileInputStream("src/test/resources/testdata.xlsx"); Workbook workbook = new XSSFWorkbook(file); Sheet sheet = workbook.getSheetAt(0); // Skip the header row, so subtract one from the physical row count int rows = sheet.getLastRowNum(); int cols = sheet.getRow(0).getPhysicalNumberOfCells(); Object[][] data = new Object[rows][cols]; for (int i = 1; i <= rows; i++) { for (int j = 0; j < cols; j++) { data[i - 1][j] = sheet.getRow(i).getCell(j).getStringCellValue(); } } workbook.close(); file.close(); return data; } }
๐ก Tip: Call getStringCellValue() only on cells formatted as text. A numeric cell throws IllegalStateException, which is the most common failure when a spreadsheet contains IDs or amounts. Use a DataFormatter to read every cell type as a String, or switch on cell.getCellType() and convert explicitly.
With the data externalised, adding a hundred more test cases becomes a spreadsheet edit rather than a code change, and the same provider can feed several test classes through the dataProviderClass attribute shown earlier.
















