【问题标题】:static Webdriver instance synchronization in javajava中的静态Webdriver实例同步
【发布时间】:2013-09-28 14:13:10
【问题描述】:

GlobalVariables 类包含在我的框架中使用的不同变量,其中一个是 WebDriver 实例:

  public class GlobalVariables
        {
        public static WebDriver driver;
    //Some other static global variables required across my framework
        public GlobalVariables(String propertiesFile)
        {
        initializeVariables(propertiesFile);
        }
        public void initializeVariables(String propertiesFile)
        {
        GlobalInitializer obj=new GlobalInitializer();
        obj.initialize(String propertiesFile);
        }
   }

GlobalInitializer 包含初始化所有 GlobalVariables 的方法:

public class GlobalInitializer extends GlobalVariables
{
public void initialize(String propertiesFile)
{
  //Some logic to read properties file and based on the properties set in it, call other initialization methods to set the global variables.
}
public void initializeDriverInstance(String Browser)
{
driver=new FireFoxDriver();
}

//其他一些初始化其他全局变量的方法。 }

我有许多 GetElement 类,它们使用驱动程序实例来获取 UI 控制元素,例如:

public class GetLabelElement extends GlobaleVariables
{
public static WebElement getLabel(String someID)
{
return driver.findElement(By.id(someId));
}
//Similar methods to get other types of label elements.
}

public class GetTextBoxElement extends GlobaleVariables
{
public static WebElement getTextBox(String someXpath)
{
return driver.findElement(By.xpath(someXpath));
}
//Similar methods to get other types of text box elements.
}

我有其他类在 UI 控件上执行一些操作(这些类也使用全局变量)例如:

public class GetLabelProperties extends GlobalVariables
{
public static String getLabelText(WebElement element)
{
return element.getText();
}
}

public class PerformAction extends GlobalVariables
{
public static void setText(String textBoxName,String someText)
{
driver.findElement(someLocator(textBoxName)).setText("someText");
}
    //Some other methods which may or may not use the global variables to perform some action
}

我在 testng 中的测试类是这样的:

public class TestClass
{
GlobalVariables globalObj=new GlobalVariables(String propertiesFile);
@Test(priority=0)
{
GlobalVariables.driver.get(someURL);
//Some assertion.
}
@Test(priority=1)
{
WebElement element=GetLabelElement.getLabel(someID);
String labelName=GetLabelProperties.getLabelText(element);
//Some assertion.
}
@Test(priority=2)
{
WebElement element=GetTextBoxElement.getTextBox(someXpath);
PerformAction.setText(element.getText(),someText);
//Some assertion.
}
}

我有多个基于场景的类似测试类。 现在,如果我单独运行这些测试,它们运行良好。但是当我尝试并行运行它们时,这些测试会以某种奇怪的方式失败。在分析时,我发现它是由每个测试初始化​​的静态全局变量,从而使其他测试失败。现在我应该如何实现我的目标,即在我的框架设计中进行最小更改的同时运行多个测试?我试过搜索选项,我遇到了一些选项,即 1)使用同步。 2)创建ThreadLocal实例(注意:我已经尝试过这个解决方案但仍然是同样的问题。测试相互混合导致失败。我已将WebDriver实例标记为ThreadLocal并覆盖了ThreadLocal的initialValue方法来初始化驱动程序实例。我仍然不确定我是否正确实施了它。)。现在我不确定如何在给定的场景中最好地实施任何一种解决方案。任何帮助表示赞赏。蒂亚!

【问题讨论】:

  • 首先我更喜欢一个简单的“命名和目录”模式来共享全局变量,我不喜欢你关于从 glob var 类扩展的解决方案!因此,使用接口(代理人员),您可以在并行情况下管理系统。

标签: java selenium selenium-webdriver testng synchronized


【解决方案1】:

我找到了解决方案:使用 ThreadLocal 是在大型多线程环境中运行测试的最佳解决方案。 代码 sn-p 在多线程环境中使用 WebDriver:

public static ThreadLocal<WebDriver> driver;
driver=new ThreadLocal<WebDriver>()
                {
                    @Override
                    protected WebDriver initialValue()
                    {
                        return new FirefoxDriver(); //You can use other driver based on your requirement.
                    }
                };

现在每次创建测试线程时都会打开一个新浏览器。 ThreadLocal 将确保每个线程只有一个静态 webdriver 实例副本。 [注意:确保您的其他全局变量也是 ThreadLocals。在我的情况下,他们不是我遇到测试错误问题的原因]。我想分享一些额外的知识,以便其他人可能会发现它的信息。在 ThreadLocal 中,每当调用 ThreadLocal.get() 方法时,您必须确保有一个用于初始化线程本地的规定,如上面 initialValue() 方法中所示,否则您可能会遇到空指针异常。谢谢大家。

【讨论】:

  • 你能展示一个更完整的代码结构吗?我似乎无法使用 ThreadLocal 使其工作。我喜欢这个想法,但我行为不端,并且浏览器的垃圾邮件实例远远超出了应有的程度。所以你:声明静态threadLocal。在类的每个构造函数中,您都覆盖了 initialValue() 方法,对吗?然后你通过threadLocalDriver.get().findElement ...等处理webdriver?
  • 我的设计是我有一个基类,其中浏览器实例的初始化发生,然后由其他类扩展。所以在基类中,我通过覆盖初始值或确保在调用 get 方法之前调用 set 方法来设置静态 threadlocal 实例的值。
【解决方案2】:

如果您要运行非并行,那么使用静态 webdriver 成员(或通过引用传递在测试类之间共享的实例)很好,因为这是不必在测试之间关闭 webdriver 实例的好方法类。但是,如果您想并行运行,则需要为每个线程拥有一个 webdriver 实例,因此在这种情况下,使用静态成员是错误的方法。相反,您需要在调用测试用例类时创建或传递一个 webdriver 实例。

此外,您正在将测试分解为测试的每个步骤的单独测试。这非常不寻常,我看不出你这样做的原因。就像人们通常做的那样,通过将所有测试步骤保留在一个测试用例中,您可以真正简化测试。

【讨论】:

  • 基于我在 javadocs 中的谷歌搜索和阅读。我发现可以使用 ThreadLocal 来实现这一点。如果我错了,现在纠正我,ThreadLocal 确保每个线程只有一个驱动程序实例在运行(即,对于多个线程,创建多个驱动程序(或浏览器)实例)。并且一个线程的驱动程序实例不会与另一个线程中运行的另一个实例混淆或混淆。正确的?请注意,我有一个非常庞大的应用程序要测试,因此我将不得不按照我上面的设计进行,为了便于使用,事情在粒度级别上被分解了很多
  • 就个人而言,我不会使用 ThreadLocal,因为您的测试框架正在执行分叉和线程处理(即 TestNG、Maven Surefire、Gradle 或其他)。如果您想手动编写自己的线程处理和测试分叉,那么您这样做很可能是因为您不了解正确的 Selenium 设计模式,或者您非常先进/知识渊博,以至于您想到了需要它的特殊情况.
【解决方案3】:

由于 JVM 处理静态成员和方法的方式,您会得到这个。

如果要并行运行,则不能有静态 webdriver 对象。

资料来源:我在我工作的地方实施的自动回归系统 - 我们遇到了这个问题。

【讨论】:

    【解决方案4】:

    你可以试试这样的

    公共类 DriverManager {

    private static final ThreadLocal<WebDriver> threadLocal = new ThreadLocal<WebDriver>();
    
    public static WebDriver getDriver() {
        return threadLocal.get();
    }
    
    public static void setDriver(WebDriver driver) {
        threadLocal.set(driver);
    }
    
    public static void closeDriver() {
        if (getDriver() != null) {
            try {
                getDriver().close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                getDriver().quit();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        threadLocal.remove();
    }
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-08
      相关资源
      最近更新 更多