DataProvider with external data in Selenium Java - Build an Automation Script
import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.FileInputStream; import java.io.IOException; import java.time.Duration; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; public class LoginTest { private WebDriver driver; private WebDriverWait wait; @BeforeClass public void setUp() { // Set path to chromedriver executable if needed driver = new ChromeDriver(); wait = new WebDriverWait(driver, Duration.ofSeconds(10)); driver.manage().window().maximize(); } @DataProvider(name = "loginData") public Object[][] getLoginData() throws IOException { String filePath = "src/test/resources/UserCredentials.xlsx"; FileInputStream fis = new FileInputStream(filePath); Workbook workbook = new XSSFWorkbook(fis); Sheet sheet = workbook.getSheet("LoginData"); int rowCount = sheet.getPhysicalNumberOfRows(); Object[][] data = new Object[rowCount - 1][2]; // exclude header row for (int i = 1; i < rowCount; i++) { Row row = sheet.getRow(i); Cell usernameCell = row.getCell(0); Cell passwordCell = row.getCell(1); data[i - 1][0] = usernameCell.getStringCellValue(); data[i - 1][1] = passwordCell.getStringCellValue(); } workbook.close(); fis.close(); return data; } @Test(dataProvider = "loginData") public void testLogin(String username, String password) { driver.get("https://example.com/login"); WebElement usernameInput = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("username"))); WebElement passwordInput = driver.findElement(By.id("password")); WebElement loginBtn = wait.until(ExpectedConditions.elementToBeClickable(By.id("loginBtn"))); usernameInput.clear(); usernameInput.sendKeys(username); passwordInput.clear(); passwordInput.sendKeys(password); loginBtn.click(); wait.until(ExpectedConditions.urlToBe("https://example.com/dashboard")); Assert.assertEquals(driver.getCurrentUrl(), "https://example.com/dashboard", "User should be redirected to dashboard after login"); WebElement logoutBtn = wait.until(ExpectedConditions.elementToBeClickable(By.id("logoutBtn"))); logoutBtn.click(); wait.until(ExpectedConditions.urlToBe("https://example.com/login")); } @AfterClass public void tearDown() { if (driver != null) { driver.quit(); } } }
This test class uses TestNG and Selenium WebDriver to automate login tests with multiple credentials from an Excel file.
setUp(): Initializes ChromeDriver and WebDriverWait before tests.
getLoginData(): Reads username and password pairs from the Excel file 'UserCredentials.xlsx' using Apache POI library. It skips the header row and returns a 2D Object array for the DataProvider.
testLogin(): For each username and password, it opens the login page, waits for input fields and login button, enters credentials, clicks login, waits for dashboard URL, asserts the URL is correct, then logs out to reset state.
tearDown(): Closes the browser after all tests.
Explicit waits ensure elements are ready before interaction. Assertions verify the expected URL after login. The Page Object Model is not fully implemented here for simplicity but can be added for better structure.
Now add data-driven testing with 3 different inputs in the Excel file and verify login success or failure message accordingly.
Practice
@DataProvider in Selenium tests?Solution
Step 1: Understand the role of DataProvider
@DataProvider is used to supply different sets of data to a test method so it can run multiple times with varied inputs.Step 2: Compare options with this purpose
Only To run the same test multiple times with different input data describes running the same test multiple times with different data, which matches the purpose of DataProvider.Final Answer:
To run the same test multiple times with different input data -> Option DQuick Check:
DataProvider = multiple test runs with data [OK]
- Confusing DataProvider with browser setup
- Thinking DataProvider locates elements
- Assuming DataProvider generates reports
Solution
Step 1: Check DataProvider method signature
A DataProvider method must return Object[][] and be public. @DataProvider(name = "csvData") public Object[][] getData() { // code to read CSV and return Object[][] } matches this.Step 2: Verify other options
@DataProvider public void getData() { // code to read CSV } returns void, which is invalid. @DataProvider(name = "csvData") public void getData() { return new Object[][] {}; } returns void but tries to return data, invalid. @DataProvider(name = "csvData") public Object getData() { // code to read CSV } returns Object, not Object[][], invalid.Final Answer:
@DataProvider(name = "csvData") public Object[][] getData() { // code to read CSV and return Object[][] } -> Option AQuick Check:
DataProvider method returns Object[][] [OK]
- Using void return type for DataProvider
- Returning Object instead of Object[][]
- Missing @DataProvider annotation name
@DataProvider(name = "csvData")
public Object[][] getData() {
return new Object[][] {
{"user1", "pass1"},
{"user2", "pass2"}
};
}
@Test(dataProvider = "csvData")
public void loginTest(String username, String password) {
System.out.println(username + ":" + password);
}Solution
Step 1: Understand DataProvider data structure
The DataProvider returns two sets of data: {"user1", "pass1"} and {"user2", "pass2"}.Step 2: Analyze test method output
The test runs twice, printing username and password separated by a colon each time, resulting in two lines: "user1:pass1" and "user2:pass2".Final Answer:
user1:pass1 user2:pass2 -> Option BQuick Check:
Each data set prints username:password [OK]
- Expecting all data printed in one line
- Confusing array structure with output format
- Assuming compilation error without reason
@DataProvider(name = "csvData")
public Object[][] getData() {
List lines = readCsv("data.csv");
Object[][] data = new Object[lines.size()][];
for (int i = 0; i <= lines.size(); i++) {
data[i] = lines.get(i);
}
return data;
}Solution
Step 1: Analyze the for loop condition
The loop runs from i = 0 to i <= lines.size(), which means it tries to access data[lines.size()] causing IndexOutOfBoundsException.Step 2: Correct the loop condition
The loop should run while i < lines.size() to stay within valid array indices.Final Answer:
The loop condition should be < instead of <= to avoid IndexOutOfBoundsException -> Option AQuick Check:
Loop index must be less than array size [OK]
- Using <= in loops causing index errors
- Returning wrong data type from DataProvider
- Misunderstanding DataProvider annotation usage
Solution
Step 1: Understand filtering before test execution
Filtering data in the DataProvider ensures only valid data sets run tests, keeping tests clean and efficient.Step 2: Implement filtering in DataProvider
Read CSV into a list, remove rows with empty first column, then convert to Object[][] to supply filtered data to tests.Final Answer:
Read all CSV rows into a List, filter out rows with empty first column, then convert filtered list to Object[][] and return -> Option CQuick Check:
Filter data in DataProvider before tests run [OK]
- Filtering data inside test method instead of DataProvider
- Returning null for filtered rows causing errors
- Skipping tests after execution instead of filtering input
Ready to prove it?
Pass the test and this pattern moves unknown
