Imagine you need to test login with 10 different username/password combinations. You could write 10 separate test methods, but that is copy-paste madness. DataProviders let you write ONE test method and feed it different data sets. The test runs once for each data row.
Think of a DataProvider as a waiter bringing different dishes to the same table. The table (test method) is the same. The dishes (data) change each time.
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.Assert;
public class LoginDataDrivenTest {
@DataProvider(name = "loginData")
public Object[][] getLoginData() {
return new Object[][] {
// username, password, shouldPass
{ "admin", "admin123", true },
{ "testuser", "password", true },
{ "invalid", "wrong", false },
{ "", "password", false },
{ "testuser", "", false },
};
}
@Test(dataProvider = "loginData")
public void testLogin(String username, String password,
boolean shouldPass) {
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.cssSelector("button[type='submit']")).click();
if (shouldPass) {
Assert.assertTrue(
driver.getCurrentUrl().contains("/dashboard"),
"Login should succeed for: " + username
);
} else {
WebElement error = wait.until(
ExpectedConditions.visibilityOfElementLocated(
By.cssSelector(".error-message")
)
);
Assert.assertTrue(error.isDisplayed(),
"Error should show for: " + username);
}
}
}This single test method runs 5 times — once for each row in the Object[][] array. In the test report, you see 5 separate test results with the specific data that was used.
In real frameworks, you do not put DataProviders in the same class as tests. You create a separate DataProvider utility class. This keeps your test classes clean.
// Separate class for all test data
public class TestData {
@DataProvider(name = "bankingUsers")
public static Object[][] bankingUsers() {
return new Object[][] {
{ "admin", "admin123", "Admin User" },
{ "john", "john456", "John Doe" },
{ "jane", "jane789", "Jane Smith" },
};
}
@DataProvider(name = "searchTerms")
public static Object[][] searchTerms() {
return new Object[][] {
{ "Laptop", 3 }, // term, expectedMinResults
{ "Phone", 5 },
{ "xyz123abc", 0 }, // no results expected
};
}
}// Test class uses DataProvider from another class
public class BankingLoginTest extends BaseTest {
@Test(dataProvider = "bankingUsers",
dataProviderClass = TestData.class)
public void testUserLogin(String username, String password,
String expectedName) {
driver.findElement(By.id("username")).sendKeys(username);
driver.findElement(By.id("password")).sendKeys(password);
driver.findElement(By.cssSelector("button[type='submit']")).click();
WebElement welcome = wait.until(
ExpectedConditions.visibilityOfElementLocated(
By.id("welcome-message")
)
);
Assert.assertTrue(
welcome.getText().contains(expectedName),
"Welcome message should contain: " + expectedName
);
}
}When using a DataProvider from a different class, the DataProvider method MUST be static. If it is not static, TestNG will throw an error because it cannot instantiate the other class to call the method.
A DataProvider can accept a java.lang.reflect.Method parameter to return different data based on which test method is calling it. One DataProvider, multiple tests, different data:
import java.lang.reflect.Method;
@DataProvider(name = "portalData")
public static Object[][] getPortalData(Method method) {
if (method.getName().equals("testBankingLogin")) {
return new Object[][] {
{ "bankuser1", "bankpass1" },
{ "bankuser2", "bankpass2" },
};
} else if (method.getName().equals("testShoppingLogin")) {
return new Object[][] {
{ "shopuser1", "shoppass1" },
{ "shopuser2", "shoppass2" },
};
}
return new Object[][] {};
}
@Test(dataProvider = "portalData")
public void testBankingLogin(String user, String pass) {
driver.get("https://www.testerrank.com/banking");
// ... login with user/pass ...
}
@Test(dataProvider = "portalData")
public void testShoppingLogin(String user, String pass) {
driver.get("https://www.testerrank.com/shopping");
// ... login with user/pass ...
}public class ShoppingCartTest extends BaseTest {
@DataProvider(name = "cartScenarios")
public Object[][] cartData() {
return new Object[][] {
// product, qty, expectedTotal
{ "Laptop", 1, "$999.99" },
{ "Laptop", 2, "$1,999.98" },
{ "T-Shirt", 3, "$89.97" },
{ "Coffee Mug", 5, "$64.95" },
};
}
@Test(dataProvider = "cartScenarios",
description = "Verify cart total calculation")
public void testCartTotal(String product, int qty,
String expectedTotal) {
driver.get("https://www.testerrank.com/shopping");
// Search and add product
driver.findElement(By.id("search")).sendKeys(product);
driver.findElement(By.id("search-btn")).click();
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.cssSelector(".product-card")
));
WebElement quantityInput = driver.findElement(By.id("quantity"));
quantityInput.clear();
quantityInput.sendKeys(String.valueOf(qty));
driver.findElement(By.id("add-to-cart")).click();
// Verify total
WebElement total = wait.until(
ExpectedConditions.visibilityOfElementLocated(
By.id("cart-total")
)
);
Assert.assertEquals(total.getText(), expectedTotal,
qty + "x " + product + " should total " + expectedTotal);
}
}In real projects, test data usually comes from Excel files, CSV files, JSON files, or databases — not hardcoded Object[][] arrays. The DataProvider method reads the external file and converts it to Object[][]. We will cover reading from external files in the Data-Driven Testing chapter later.
Q: What is a DataProvider? How have you used it?
A: A DataProvider is a method annotated with @DataProvider that returns a 2D Object array (Object[][]). Each row is a set of parameters passed to the test method. The test runs once per row. I used DataProviders for: (1) testing login with multiple valid and invalid credentials, (2) testing search with different keywords and expected result counts, (3) testing form submissions with various inputs. I keep DataProviders in a separate class using dataProviderClass to keep test classes clean. The DataProvider method must be static when used from another class.
Key Point: DataProviders let you run one test method with multiple data sets. Return Object[][] and link with @Test(dataProvider = "name").