【问题标题】:Is ThreadLocal required for automation framework that uses Cucumber, Jmeter and FailSafe?使用 Cucumber、Jmeter 和 FailSafe 的自动化框架是否需要 ThreadLocal?
【发布时间】:2021-04-08 23:28:48
【问题描述】:

对不起,如果这个问题听起来很愚蠢。 我们正在开发一个使用 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();
    }

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

【问题讨论】:

    标签: java multithreading selenium automated-tests thread-local


    【解决方案1】:

    视情况而定。

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

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

    因此,通过使用static WebDriver driver,您将拥有一个用于所有测试和所有线程的网络驱动程序。通过使用static ThreadLocal&lt;WebDriver&gt; driver,每个线程只有一个 webdriver,从而在该线程上执行的场景之间有效地共享 webdriver。

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

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

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

    这意味着您现在遇到了步骤之间共享信息的问题。您正在使用静态字段来共享网络驱动程序。但是你不能使用静态字段,因为现在有多个线程在运行。

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

        <dependency>
            <groupId>io.cucumber</groupId>
            <artifactId>cucumber-picocontainer</artifactId>
            <version>${cucumber.version}</version>
            <scope>test</scope>
        </dependency>
    

    因此,例如,当您的步骤定义依赖于 WebDriverFactory 时,cucumber 将为您实例化 WebDriverFactory 并使用同一个工厂实例化 StepDefinitionOtherStepDefinition

    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
        }
    
    }
    

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

    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
        } 
    }
    

    【讨论】:

    • M.P.Korstanje,非常感谢您的回答。真的很有用。
    猜你喜欢
    • 1970-01-01
    • 2015-12-21
    • 1970-01-01
    • 2016-07-10
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多