TestNG DataProvider

@DataProvider

public class LoginDataTest extends BaseTest {

    @DataProvider(name = "logins")
    public Object[][] logins() {
        return new Object[][] {
            {"[email protected]", "Test@123", "success"},
            {"[email protected]", "wrong",    "Invalid email or password"},
            {"[email protected]", "Test@123", "Your account is locked"},
        };
    }

    @Test(dataProvider = "logins")
    public void login(String email, String password, String expected) {
        driver.get(BASE_URL + "/login");
        driver.findElement(By.id("email")).sendKeys(email);
        driver.findElement(By.id("password")).sendKeys(password);
        driver.findElement(By.id("login-btn")).click();
        String actual = expected.equals("success")
                ? (wait.until(ExpectedConditions.urlContains("/account")) ? "success" : "fail")
                : wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".error-message"))).getText();
        Assert.assertEquals(actual, expected);
    }
}

Result

Example

Each row is reported as its own test:

mvn test -Dtest=LoginDataTest

Example output

PASSED: login("[email protected]", "Test@123", "success")
PASSED: login("[email protected]", "wrong", "Invalid email or password")
PASSED: login("[email protected]", "Test@123", "Your account is locked")
===============================================
    Default test
    Tests run: 3, Failures: 0, Skips: 0
===============================================

Tip: @DataProvider(parallel = true) runs rows in parallel (needs a thread-safe driver setup, covered in the advance level). A data provider in another class is used with dataProviderClass = TestData.class.

A Mini Data Provider

Example

Under the hood, TestNG calls the data provider, then calls the test method once per row and records each result. This runnable simulation does the same with a lambda as the "test". The fake app knows only Priya, so the last row fails while the others still run:

import java.util.*;

public class Main {
    interface LoginTest { String run(String email, String password); }

    static Object[][] logins() {
        return new Object[][] {
            {"[email protected]", "Test@123", "success"},
            {"[email protected]", "wrong", "error"},
            {"", "Test@123", "error"},
            {"[email protected]", "Test@123", "success"},
        };
    }

    public static void main(String[] args) {
        Set<String> registered = Set.of("[email protected]");
        LoginTest fakeApp = (email, pw) -> registered.contains(email) && pw.equals("Test@123") ? "success" : "error";

        int pass = 0, fail = 0;
        for (Object[] row : logins()) {
            String actual = fakeApp.run((String) row[0], (String) row[1]);
            boolean ok = actual.equals(row[2]);
            if (ok) pass++; else fail++;
            System.out.printf("%s: login(\"%s\", \"%s\") expected=%s actual=%s%n",
                    ok ? "PASSED" : "FAILED", row[0], row[1], row[2], actual);
        }
        System.out.println("Tests run: " + (pass + fail) + ", Failures: " + fail);
    }
}

Output

PASSED: login("[email protected]", "Test@123") expected=success actual=success
PASSED: login("[email protected]", "wrong") expected=error actual=error
PASSED: login("", "Test@123") expected=error actual=error
FAILED: login("[email protected]", "Test@123") expected=success actual=error
Tests run: 4, Failures: 1