使用ThreadLocal并行测试中线程ID不匹配和NullPointerException问题

问题描述 投票:0回答:1

我正在使用 TestNG 和 Cucumber 运行并行测试。我正在使用 ThreadLocal 管理驱动程序对象。但是,我注意到 setDriver 和 getDriver 方法在不同的线程 ID 上运行,这导致 getDriver() 返回 null。问题似乎是并行测试中不同线程之间的不兼容。在调试期间,setDriver 和 getDriver 在不同的线程 ID 上运行。当我禁用并行操作时,测试工作正常。不过ThreadLocal在并行模式下好像不兼容。

package utilities;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;

import java.net.URL;
import java.time.Duration;

public class DriverManager {

    private static ThreadLocal<AndroidDriver> driver = new ThreadLocal<>();

    public static void setDriver (String deviceName,String platformName,String platformVersion,String port) {
        if (driver.get() == null) {
            try {
                System.out.println("setDriver - Thread ID: " + Thread.currentThread().getId());
                UiAutomator2Options options = new UiAutomator2Options();
                options.setPlatformName(platformName);
                options.setPlatformVersion(platformVersion);
                options.setDeviceName(deviceName);
                options.setAppPackage(ConfigReader.getProperty("appPackage"));
                options.setAppActivity(ConfigReader.getProperty("appActivity"));
                options.setAutomationName("UiAutomator2");
                options.setNewCommandTimeout(Duration.ofSeconds(3600));
                options.setAutoGrantPermissions(true);
                options.setApp(ConfigReader.getProperty("app"));
                options.setFullReset(true);
                String appiumServerURL = ConfigReader.getProperty("appiumServerURL");
                //String url="http://"+ConfigReader.getProperty("ip")+":"+port+"/wd/hub";
                AndroidDriver driverInstance = new AndroidDriver(new URL(appiumServerURL), options);
                driverInstance.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
                driver.set(driverInstance);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException("Driver başlatılamadı: " + e.getMessage());
            }
        }
    }

    public static AndroidDriver getDriver() {
        System.out.println("getDriver - Thread ID: " + Thread.currentThread().getId());
        return driver.get();
    }
    public static void quitDriver() {
        if (driver.get() != null) {
            driver.get().quit();
            driver.remove();
        }
    }



}

package utilities;

import io.cucumber.testng.AbstractTestNGCucumberTests;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
public class TestBase extends AbstractTestNGCucumberTests {

    @BeforeTest
    @Parameters({"deviceName","platformName","platformVersion","port"})
    public void beforeMethod(String deviceName,String platformName,String platformVersion,String port){
        try {
            System.out.println("TestNG: @BeforeTest çalıştı.");
            DriverManager.setDriver(deviceName,platformName,platformVersion,port);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

package runners;


import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
import org.testng.annotations.DataProvider;
import utilities.TestBase;

@CucumberOptions(
        features = "src/test/resources/features",
        glue = {"steps","utilities"},
        plugin = {"pretty","html:target/cucumber-reports-testng.html"},
        monochrome = true,
        dryRun = false,
        tags = "@hepsiburada1"
)

public class TestNGRunner extends TestBase {
@Override
    @DataProvider(parallel = true)
    public Object[][] scenarios() { return super.scenarios(); }
}

package runners;


import io.cucumber.testng.AbstractTestNGCucumberTests;
import io.cucumber.testng.CucumberOptions;
import org.testng.annotations.DataProvider;
import utilities.TestBase;

@CucumberOptions(
        features = "src/test/resources/features",
        glue = {"steps","utilities"},
        plugin = {"pretty","html:target/cucumber-reports-testng.html"},
        monochrome = true,
        dryRun = false,
        tags = "@hepsiburada2"
)

public class TestNGRunner2 extends TestBase {
    @Override
    @DataProvider(parallel = true)
    public Object[][] scenarios() { return super.scenarios(); }
}

另外,Parameters注释看不到我在testng.xml文件中给出的参数

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Cucumber Test Suite" parallel="tests" thread-count="2">
    <test name="Cucumber Tests">
        <parameter name="deviceName" value="emulator-5554"></parameter>
        <parameter name="platformName" value="Android"></parameter>
        <parameter name="platformVersion" value="14.0"></parameter>
        <parameter name="port" value="4723"></parameter>
        <classes>
            <class name="runners.TestNGRunner" />
        </classes>
    </test>
    <test name="Cucumber Tests 2">
        <parameter name="deviceName" value="emulator-5556"></parameter>
        <parameter name="platformName" value="Android"></parameter>
        <parameter name="platformVersion" value="14.0"></parameter>
        <parameter name="port" value="4725"></parameter>
        <classes>
            <class name="runners.TestNGRunner" />
        </classes>
    </test>
</suite>
selenium-webdriver cucumber testng thread-local parallel-tests
1个回答
0
投票

这在某种程度上是预料之中的。

@BeforeTest
方法在所有场景之前运行一次。当场景并行运行时,它们将是一个不同的线程。

而是内联

AbstractTestNGCucumberTests
并将 Web 驱动程序的配置存储在运行程序类中。然后,在每个场景执行之前,在
runScenario
方法中,根据关闭的运行程序中存储的配置创建 Web 驱动程序。

© www.soinside.com 2019 - 2024. All rights reserved.