You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多设备并行执行Appium自动化脚本时,如何为各线程参数化测试数据?

如何在Appium+Java+TestNG多线程并行测试中为每个线程传入不同测试数据?

当然可以通过TestNG XML为每个并行线程传递不同的测试数据,而且结合Appium的多设备场景,还有几种实用的实现方式,我来给你详细拆解:


方式一:通过TestNG XML的<test>和<parameter>标签绑定设备与测试数据

这种方式适合设备数量固定、每个设备对应一组测试数据的场景,核心是给每个设备单独定义一个<test>节点,在节点内配置专属参数(包括设备信息和测试数据路径),然后让TestNG按test级别并行执行。

示例TestNG XML配置

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="Appium多设备并行测试套件" parallel="tests" thread-count="2">
    <!-- 设备1对应的测试任务 -->
    <test name="模拟器1登录测试">
        <parameter name="udid" value="emulator-5554"/>
        <parameter name="platformVersion" value="13"/>
        <parameter name="testDataFile" value="src/test/resources/testdata/user1.json"/>
        <classes>
            <class name="com.automation.tests.LoginTest"/>
        </classes>
    </test>
    <!-- 设备2对应的测试任务 -->
    <test name="模拟器2登录测试">
        <parameter name="udid" value="emulator-5556"/>
        <parameter name="platformVersion" value="14"/>
        <parameter name="testDataFile" value="src/test/resources/testdata/user2.json"/>
        <classes>
            <class name="com.automation.tests.LoginTest"/>
        </classes>
    </test>
</suite>

测试类接收参数并执行

在测试类中使用@Parameters注解接收XML里的参数,初始化Appium Driver时绑定设备,同时加载对应测试数据:

public class LoginTest {
    private AndroidDriver<MobileElement> driver;
    private UserTestData testData;

    @Parameters({"udid", "platformVersion", "testDataFile"})
    @BeforeMethod
    public void setUp(String udid, String platformVersion, String testDataPath) throws MalformedURLException {
        // 加载当前线程对应的测试数据
        testData = loadTestData(testDataPath);
        
        // 初始化Appium Driver,绑定指定设备
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability(MobileCapabilityType.UDID, udid);
        caps.setCapability(MobileCapabilityType.PLATFORM_VERSION, platformVersion);
        caps.setCapability(MobileCapabilityType.APP_PACKAGE, "com.example.app");
        caps.setCapability(MobileCapabilityType.APP_ACTIVITY, ".LoginActivity");
        
        driver = new AndroidDriver<>(new URL("http://localhost:4723/wd/hub"), caps);
    }

    @Test
    public void executeLoginTest() {
        // 使用专属测试数据执行登录操作
        driver.findElement(By.id("username_input")).sendKeys(testData.getUsername());
        driver.findElement(By.id("password_input")).sendKeys(testData.getPassword());
        driver.findElement(By.id("login_btn")).click();
        
        // 断言登录结果
        Assert.assertTrue(driver.findElement(By.id("home_page")).isDisplayed());
    }

    @AfterMethod
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }

    // 读取JSON测试数据并转为实体类
    private UserTestData loadTestData(String filePath) {
        try {
            return new ObjectMapper().readValue(new File(filePath), UserTestData.class);
        } catch (IOException e) {
            throw new RuntimeException("加载测试数据失败", e);
        }
    }
}

方式二:结合@DataProvider实现动态多线程数据驱动

如果你的设备或测试数据数量较多,不想在XML里逐个编写<test>节点,可以用TestNG的@DataProvider注解,开启并行模式后,每一组数据会自动分配到独立线程执行,同时可以灵活绑定设备与测试数据。

定义并行DataProvider

创建一个数据提供者类,返回包含设备信息和测试数据的二维数组(也可以从配置文件读取,避免硬编码):

public class ParallelTestDataProvider {

    @DataProvider(name = "deviceTestData", parallel = true)
    public Object[][] getDeviceAndTestData() {
        // 这里可以从properties/JSON配置文件读取设备列表和对应测试数据,动态生成数组
        return new Object[][]{
                {"emulator-5554", "13", "user1.json"},
                {"emulator-5556", "14", "user2.json"},
                {"real-device-xxxx", "12", "user3.json"}
        };
    }
}

测试类使用DataProvider

在测试方法上指定使用该数据提供者,直接接收每组数据并执行测试:

public class LoginTest {

    @Test(dataProvider = "deviceTestData", dataProviderClass = ParallelTestDataProvider.class)
    public void executeLoginTest(String udid, String platformVersion, String testDataFileName) throws MalformedURLException {
        // 加载测试数据
        String testDataPath = "src/test/resources/testdata/" + testDataFileName;
        UserTestData testData = loadTestData(testDataPath);
        
        // 初始化Appium Driver
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability(MobileCapabilityType.UDID, udid);
        caps.setCapability(MobileCapabilityType.PLATFORM_VERSION, platformVersion);
        caps.setCapability(MobileCapabilityType.APP_PACKAGE, "com.example.app");
        caps.setCapability(MobileCapabilityType.APP_ACTIVITY, ".LoginActivity");
        
        AndroidDriver<MobileElement> driver = new AndroidDriver<>(new URL("http://localhost:4723/wd/hub"), caps);
        
        // 执行测试逻辑
        driver.findElement(By.id("username_input")).sendKeys(testData.getUsername());
        driver.findElement(By.id("password_input")).sendKeys(testData.getPassword());
        driver.findElement(By.id("login_btn")).click();
        
        Assert.assertTrue(driver.findElement(By.id("home_page")).isDisplayed());
        
        // 清理资源
        driver.quit();
    }

    private UserTestData loadTestData(String filePath) {
        try {
            return new ObjectMapper().readValue(new File(filePath), UserTestData.class);
        } catch (IOException e) {
            throw new RuntimeException("加载测试数据失败", e);
        }
    }
}

配套TestNG XML配置

只需要设置按methods级别并行,线程数匹配数据组数即可:

<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="Appium DataProvider并行套件" parallel="methods" thread-count="3">
    <test name="多设备登录测试">
        <classes>
            <class name="com.automation.tests.LoginTest"/>
        </classes>
    </test>
</suite>

关键注意事项

  • Appium多会话支持:单Appium服务器默认支持多会话,但如果设备数量较多,建议启动多个Appium服务实例(使用不同端口,比如4723、4725、4727),避免会话冲突。
  • 数据隔离:不要使用静态变量存储测试数据或Driver实例,确保每个线程持有独立的资源,避免线程安全问题。
  • 资源清理:每个测试执行完成后务必调用driver.quit()释放设备资源,否则会导致后续测试无法连接设备。

内容的提问来源于stack exchange,提问作者Abdulla Abuhammam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 13:03:11