使用 Cucumber、Jmeter 和 FailSafe 的自动化框架是否需要 ThreadLocal?

Oas*_*001 3 java selenium multithreading automated-tests thread-local

对不起,如果这个问题听起来很愚蠢。我们正在开发一个使用 Java、Cucumber、Junit 和 Failsafe 套件等的自动化框架。有人建议使用 ThreadLocal。但是我有点困惑,为什么当 Junit 在自己的线程中运行黄瓜功能时我们需要使用 ThreadLocal ..

建议是使用 ThreadLocal ,如下所示;-

public class WebDriverFactory {

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

    public static synchronized void setDriver(String browser) {

        switch (browser) {
            case "chrome":
                driver = ThreadLocal.withInitial(() -> {
                    WebDriverManager.chromedriver().setup();
                    return new ChromeDriver(BrowserOptions.getChromeOptions());
                });
        
                break;
           
            default:
                throw new IllegalStateException("Unexpected value: " + browser);
        }
    }

    public static synchronized WebDriver getDriver(){
        return driver.get();
    }
Run Code Online (Sandbox Code Playgroud)

任何人都可以确认这是否真的需要并行运行测试。?另外,使用 ThreadLocal 时是否需要“同步”?

mpk*_*nje 6

这取决于。

使用静态字段时,JVM 中只有该字段的单个实例。它指的是内存中的一个特定地址。与引用特定对象字段的对象字段不同,对于每个对象,该字段在内存中都有一个唯一地址。

当使用一个 ThreadLocal每个线程都有自己的变量实例。

因此,通过使用static WebDriver driver您可以为所有测试和所有线程提供一个网络驱动程序。通过使用static ThreadLocal<WebDriver> driver,每个线程只有一个 webdriver,可以在该线程上执行的场景之间有效地共享 webdriver。

当并行执行测试时,有多个线程在执行场景,因此单个 webdriver 会出现问题。并行运行的场景会使 webdriver 同时做不同的事情,或者他们必须等待 webdriver 可用,使它们再次有效地串行运行。

因此,如果要在场景之间共享 webdriver 并且这些场景并行运行,则必须使用ThreadLocal.

然而,似乎不熟悉编程并发系统。因此,您可能需要考虑不同的方法。与其在场景之间共享 webdriver,不如考虑在每个场景中启动一个新的 webdriver。这更安全,从测试的角度来看也更清晰,因为每个场景开始时都没有前一个场景的任何状态。

这意味着您现在面临在步骤之间共享信息的问题。您正在使用静态字段共享来共享 webdriver。但是您不能使用静态字段,因为现在有多个线程在运行。

为了解决这个问题,Cucumber 支持依赖注入。最容易使用的可能是cucumber-pico。使用依赖注入时,cucumber 将使用一组独立的依赖项为每个场景实例化每个步骤定义类。

    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-picocontainer</artifactId>
        <version>${cucumber.version}</version>
        <scope>test</scope>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

因此,例如,当您的步骤定义依赖于 a 时WebDriverFactory,cucumber 将为WebDriverFactory您实例化 aStepDefinitionOtherStepDefinition使用相同的工厂实例化两者。

public class StepDefinition {

    private final WebDriverFactory webdriverFactory;

    public StepDefinitions(WebDriverFactory webdriverFactory) {
        this.webdriverFactory = webdriverFactory;
    }

    @Given("I do a thing with a webdriver")
    public void useTheWebDriver() {
        // get the webdriver from the factory and use it
    }

}

public class OtherStepDefinition {

    private final WebDriverFactory webdriverFactory; // Same instance as in StepDefinition

    public OtherStepDefinition(WebDriverFactory webdriverFactory) {
        this.webdriverFactory = webdriverFactory; 
    }

    @Given("I do another thing with a webdriver")
    public void useTheWebDriver() {
        // get the webdriver from the factory and use it
    }

}
Run Code Online (Sandbox Code Playgroud)

然后在 web 驱动程序工厂中,您保留对 webdriver 的引用,以便在两个步骤定义中使用。

public class WebDriverFactory implements Startable {

    private WebDriver driver;

    public setDriver(String browser) {
        // create the web driver here 
    }

    public static synchronized WebDriver getDriver(){
        return driver;
    }

    public void start() { 
      // do nothing
    } 
    
    public void stop() { 
       // stop web driver if it was created
    } 
}
Run Code Online (Sandbox Code Playgroud)