TestNG数据驱动测试Skill testng-data-driven

这个技能用于实现数据驱动测试,通过TestNG框架的DataProviders、工厂模式和参数化技术,提高测试覆盖率和效率。它支持从多个数据源(如CSV、JSON、Excel)加载测试数据,并适用于跨环境、并行测试等场景。关键词:TestNG, 数据驱动测试, DataProvider, 工厂模式, 参数化, 测试自动化, Java测试, 测试覆盖, 外部数据源。

测试 0 次安装 0 次浏览 更新于 3/25/2026

name: TestNG数据驱动测试 user-invocable: false description: 在实现数据驱动测试时使用,包括TestNG DataProviders、工厂方法和参数化模式。 allowed-tools: [Read, Write, Edit, Bash, Glob, Grep]

TestNG 数据驱动测试

掌握TestNG数据驱动测试,包括DataProviders、工厂模式和参数化,以实现全面的测试覆盖。此技能涵盖使用多个数据集高效测试的技术。

概述

数据驱动测试将测试逻辑与测试数据分离,使单个测试方法可以针对多个输入运行。TestNG通过DataProviders、工厂方法和XML参数化提供了强大的数据驱动测试功能。

DataProvider基础

简单DataProvider

import org.testng.annotations.*;
import static org.testng.Assert.*;

public class SimpleDataProviderTest {

    @DataProvider(name = "numbers")
    public Object[][] provideNumbers() {
        return new Object[][] {
            {1, 2, 3},
            {4, 5, 9},
            {0, 0, 0},
            {-1, 1, 0}
        };
    }

    @Test(dataProvider = "numbers")
    public void testAddition(int a, int b, int expected) {
        assertEquals(a + b, expected);
    }
}

单值DataProvider

public class SingleValueDataProviderTest {

    @DataProvider(name = "validEmails")
    public Object[][] provideValidEmails() {
        return new Object[][] {
            {"user@example.com"},
            {"test.user@domain.org"},
            {"admin+tag@company.co.uk"},
            {"user123@test.io"}
        };
    }

    @Test(dataProvider = "validEmails")
    public void testValidEmail(String email) {
        assertTrue(isValidEmail(email), "Email should be valid: " + email);
    }

    private boolean isValidEmail(String email) {
        return email != null && email.matches("^[\\w+.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$");
    }
}

高级DataProvider模式

独立类中的DataProvider

// DataProviders类
public class TestDataProviders {

    @DataProvider(name = "userData")
    public static Object[][] provideUserData() {
        return new Object[][] {
            {"john", "john@example.com", 25},
            {"jane", "jane@example.com", 30},
            {"bob", "bob@example.com", 22}
        };
    }

    @DataProvider(name = "loginCredentials")
    public static Object[][] provideLoginCredentials() {
        return new Object[][] {
            {"admin", "admin123", true},
            {"user", "user456", true},
            {"invalid", "wrong", false}
        };
    }
}

// 使用外部DataProvider的测试类
public class UserTest {

    @Test(dataProvider = "userData", dataProviderClass = TestDataProviders.class)
    public void testUserCreation(String name, String email, int age) {
        User user = new User(name, email, age);
        assertNotNull(user);
        assertEquals(user.getName(), name);
        assertEquals(user.getEmail(), email);
        assertEquals(user.getAge(), age);
    }

    @Test(dataProvider = "loginCredentials", dataProviderClass = TestDataProviders.class)
    public void testLogin(String username, String password, boolean expectedSuccess) {
        boolean result = authService.login(username, password);
        assertEquals(result, expectedSuccess);
    }
}

带上下文方法的DataProvider

import org.testng.ITestContext;
import org.testng.annotations.*;
import java.lang.reflect.Method;

public class ContextAwareDataProviderTest {

    @DataProvider(name = "contextAwareData")
    public Object[][] provideContextAwareData(ITestContext context, Method method) {
        String testName = method.getName();
        String suiteName = context.getSuite().getName();

        System.out.println("Providing data for: " + testName + " in suite: " + suiteName);

        if (testName.contains("Admin")) {
            return new Object[][] {
                {"admin", "ADMIN_ROLE"},
                {"superadmin", "SUPER_ADMIN_ROLE"}
            };
        } else {
            return new Object[][] {
                {"user1", "USER_ROLE"},
                {"user2", "USER_ROLE"}
            };
        }
    }

    @Test(dataProvider = "contextAwareData")
    public void testAdminAccess(String username, String role) {
        // 测试管理员访问
    }

    @Test(dataProvider = "contextAwareData")
    public void testUserAccess(String username, String role) {
        // 测试用户访问
    }
}

并行DataProvider

public class ParallelDataProviderTest {

    @DataProvider(name = "parallelData", parallel = true)
    public Object[][] provideParallelData() {
        return new Object[][] {
            {"Test 1"},
            {"Test 2"},
            {"Test 3"},
            {"Test 4"},
            {"Test 5"},
            {"Test 6"},
            {"Test 7"},
            {"Test 8"},
            {"Test 9"},
            {"Test 10"}
        };
    }

    @Test(dataProvider = "parallelData")
    public void testParallel(String data) {
        System.out.println(Thread.currentThread().getName() + " - " + data);
        // 测试在并行线程中运行
    }
}

迭代器DataProvider

import java.util.Iterator;
import java.util.Arrays;

public class IteratorDataProviderTest {

    @DataProvider(name = "iteratorData")
    public Iterator<Object[]> provideIteratorData() {
        // 适用于大型数据集 - 按需生成数据
        return Arrays.asList(
            new Object[]{"data1", 1},
            new Object[]{"data2", 2},
            new Object[]{"data3", 3}
        ).iterator();
    }

    @Test(dataProvider = "iteratorData")
    public void testWithIterator(String name, int value) {
        assertNotNull(name);
        assertTrue(value > 0);
    }
}

大型数据集的懒加载DataProvider

import java.util.Iterator;
import java.util.NoSuchElementException;

public class LazyDataProviderTest {

    @DataProvider(name = "lazyData")
    public Iterator<Object[]> provideLazyData() {
        return new Iterator<Object[]>() {
            private int current = 0;
            private final int max = 1000;

            @Override
            public boolean hasNext() {
                return current < max;
            }

            @Override
            public Object[] next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }
                // 按需生成数据
                return new Object[]{
                    "User_" + current,
                    "user" + current + "@example.com",
                    current++
                };
            }
        };
    }

    @Test(dataProvider = "lazyData")
    public void testLargeDataset(String name, String email, int id) {
        assertNotNull(name);
        assertTrue(email.contains("@"));
    }
}

工厂模式

基础工厂

import org.testng.annotations.*;

public class FactoryTest {
    private String browser;

    @Factory
    public static Object[] createInstances() {
        return new Object[] {
            new FactoryTest("chrome"),
            new FactoryTest("firefox"),
            new FactoryTest("safari")
        };
    }

    public FactoryTest(String browser) {
        this.browser = browser;
    }

    @Test
    public void testHomePage() {
        System.out.println("Testing home page on: " + browser);
    }

    @Test
    public void testLoginPage() {
        System.out.println("Testing login page on: " + browser);
    }
}

带DataProvider的工厂

public class FactoryWithDataProviderTest {
    private String username;
    private String role;

    @Factory(dataProvider = "userRoles")
    public FactoryWithDataProviderTest(String username, String role) {
        this.username = username;
        this.role = role;
    }

    @DataProvider(name = "userRoles")
    public static Object[][] provideUserRoles() {
        return new Object[][] {
            {"admin", "ADMIN"},
            {"manager", "MANAGER"},
            {"user", "USER"},
            {"guest", "GUEST"}
        };
    }

    @Test
    public void testUserPermissions() {
        System.out.println("Testing permissions for " + username + " with role " + role);
        // 基于角色测试特定权限
    }

    @Test
    public void testUserDashboard() {
        System.out.println("Testing dashboard for " + username + " with role " + role);
        // 基于角色测试仪表板功能
    }

    @Override
    public String toString() {
        return username + "_" + role;
    }
}

外部数据源

CSV DataProvider

import java.io.*;
import java.util.*;

public class CsvDataProviderTest {

    @DataProvider(name = "csvData")
    public Object[][] provideCsvData() throws IOException {
        List<Object[]> data = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(
                new FileReader("src/test/resources/testdata.csv"))) {

            String line;
            // 跳过标题行
            reader.readLine();

            while ((line = reader.readLine()) != null) {
                String[] values = line.split(",");
                data.add(new Object[]{
                    values[0].trim(),  // 用户名
                    values[1].trim(),  // 邮箱
                    Integer.parseInt(values[2].trim())  // 年龄
                });
            }
        }

        return data.toArray(new Object[0][]);
    }

    @Test(dataProvider = "csvData")
    public void testUserFromCsv(String username, String email, int age) {
        assertNotNull(username);
        assertTrue(email.contains("@"));
        assertTrue(age > 0);
    }
}

JSON DataProvider

import com.fasterxml.jackson.databind.*;
import java.io.*;
import java.util.*;

public class JsonDataProviderTest {

    @DataProvider(name = "jsonData")
    public Object[][] provideJsonData() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        List<Map<String, Object>> testData = mapper.readValue(
            new File("src/test/resources/testdata.json"),
            new TypeReference<List<Map<String, Object>>>() {}
        );

        Object[][] data = new Object[testData.size()][3];
        for (int i = 0; i < testData.size(); i++) {
            Map<String, Object> entry = testData.get(i);
            data[i][0] = entry.get("name");
            data[i][1] = entry.get("email");
            data[i][2] = ((Number) entry.get("age")).intValue();
        }

        return data;
    }

    @Test(dataProvider = "jsonData")
    public void testUserFromJson(String name, String email, int age) {
        assertNotNull(name);
        assertFalse(name.isEmpty());
    }
}

Excel DataProvider

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.*;

public class ExcelDataProviderTest {

    @DataProvider(name = "excelData")
    public Object[][] provideExcelData() throws IOException {
        List<Object[]> data = new ArrayList<>();

        try (FileInputStream fis = new FileInputStream("src/test/resources/testdata.xlsx");
             Workbook workbook = new XSSFWorkbook(fis)) {

            Sheet sheet = workbook.getSheetAt(0);

            for (int i = 1; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);
                if (row != null) {
                    data.add(new Object[]{
                        getCellValue(row.getCell(0)),
                        getCellValue(row.getCell(1)),
                        (int) row.getCell(2).getNumericCellValue()
                    });
                }
            }
        }

        return data.toArray(new Object[0][]);
    }

    private String getCellValue(Cell cell) {
        if (cell == null) return "";
        return cell.getCellType() == CellType.STRING
            ? cell.getStringCellValue()
            : String.valueOf((int) cell.getNumericCellValue());
    }

    @Test(dataProvider = "excelData")
    public void testUserFromExcel(String name, String email, int age) {
        assertNotNull(name);
        assertTrue(age >= 0);
    }
}

高级参数化

可选参数

public class OptionalParameterTest {

    @Parameters({"browser", "timeout"})
    @BeforeClass
    public void setUp(
            String browser,
            @Optional("30000") String timeout) {

        System.out.println("Browser: " + browser);
        System.out.println("Timeout: " + timeout);
    }

    @Test
    public void testMethod() {
        // 测试实现
    }
}

结合参数和DataProviders

public class CombinedParametersTest {

    private String environment;

    @Parameters("environment")
    @BeforeClass
    public void setUp(String environment) {
        this.environment = environment;
    }

    @DataProvider(name = "users")
    public Object[][] provideUsers() {
        return new Object[][] {
            {"user1", "pass1"},
            {"user2", "pass2"}
        };
    }

    @Test(dataProvider = "users")
    public void testLogin(String username, String password) {
        System.out.println("Testing on " + environment + " with user: " + username);
        // 结合XML参数和DataProvider数据
    }
}

基于参数的动态DataProvider

public class DynamicDataProviderTest {

    private static String environment;

    @Parameters("environment")
    @BeforeClass
    public static void setUp(@Optional("staging") String env) {
        environment = env;
    }

    @DataProvider(name = "environmentData")
    public Object[][] provideEnvironmentData() {
        switch (environment) {
            case "production":
                return new Object[][] {
                    {"prod-user1", "prod-api-key-1"},
                    {"prod-user2", "prod-api-key-2"}
                };
            case "staging":
                return new Object[][] {
                    {"staging-user1", "staging-api-key-1"},
                    {"staging-user2", "staging-api-key-2"}
                };
            default:
                return new Object[][] {
                    {"dev-user", "dev-api-key"}
                };
        }
    }

    @Test(dataProvider = "environmentData")
    public void testApiAccess(String user, String apiKey) {
        System.out.println("Testing API access for: " + user + " on " + environment);
    }
}

DataProvider命名

命名DataProvider行

public class NamedDataProviderTest {

    @DataProvider(name = "testCases")
    public Object[][] provideTestCases() {
        return new Object[][] {
            // 每行可以有一个名称以便更好报告
            {"TC001_ValidLogin", "admin", "admin123", true},
            {"TC002_InvalidPassword", "admin", "wrong", false},
            {"TC003_EmptyUsername", "", "password", false},
            {"TC004_SpecialCharacters", "user@#$", "pass@#$", false}
        };
    }

    @Test(dataProvider = "testCases")
    public void testLogin(String testCaseId, String username, String password, boolean expected) {
        System.out.println("Executing: " + testCaseId);
        // 测试实现
    }
}

最佳实践

  1. 保持DataProviders专注 - 每个数据类型或场景一个DataProvider
  2. 对大型数据集使用外部文件 - CSV、JSON或Excel以提高可维护性
  3. 共享时使DataProviders静态 - 外部DataProvider类所需
  4. 包含测试用例标识符 - 第一列可以是测试用例ID用于追踪
  5. 使用有意义的DataProvider名称 - 清晰名称改进测试报告
  6. 考虑并行执行 - 为独立测试启用并行标志
  7. 显式处理空值 - 定义空/无数据的行为
  8. 文档化预期格式 - 为复杂提供者注释数据结构
  9. 对大型数据集使用迭代器 - 通过懒加载避免内存问题
  10. 分离测试数据与测试逻辑 - 保持测试整洁和数据可管理

常见陷阱

  1. 类型不匹配 - DataProvider类型必须匹配测试方法参数
  2. 缺少静态修饰符 - 外部DataProvider方法必须是静态的
  3. 数组维度错误 - 必须返回Object[][]而非Object[]
  4. 空指针异常 - 未处理数据中的空值
  5. 资源泄漏 - 未在DataProviders中关闭文件流
  6. 紧耦合 - 硬编码环境特定数据
  7. 过度参数化 - 太多参数使测试难以阅读
  8. 缺少测试用例ID - 没有标识符难以追踪失败
  9. 忽略执行顺序 - DataProvider行可能以任何顺序运行
  10. 未考虑并行性 - 并行DataProviders中的共享状态问题

何时使用此技能

  • 测试多个输入组合
  • 实现边界值测试
  • 创建跨浏览器或跨环境测试
  • 从外部源加载测试数据
  • 构建参数化测试套件
  • 创建数据驱动API测试
  • 实现数据库驱动测试
  • 构建可重用测试数据框架