【问题标题】:Selenium tests doesn't work when run in Jmeter with Java Sampler and multiple threads使用 Java Sampler 和多线程在 Jmeter 中运行时,Selenium 测试不起作用
【发布时间】:2015-03-17 17:03:07
【问题描述】:

我正在使用 Jmeter 和 Java Sampler 为网站运行 Selenium 测试。 我已经使用“Eclipse ->Export to jar”导出了扩展 AbstractJavaSamplerClient 的测试类,并将其复制到 Jmeter/lib/ext。我在测试中使用来自另一个 java 项目的类。我将这个项目打包到 .jar 并复制到 Jmeter/lib。 当我在 Jmeter 中以 1 个线程运行测试时,它工作得很好,但如果我运行 2 个或更多线程,Selenium Web 驱动程序无法找到元素,但我看到它们是可见的。我是新手,但看起来 Selenium 不能在多个线程中工作。 我究竟做错了什么?请帮忙。

【问题讨论】:

    标签: multithreading selenium jmeter


    【解决方案1】:

    根据How to Use JUnit With JMeter 指南,某些限制适用于 JMeter 的 JUnit 采样器

    1. 仅记录 JUnit 请求采样器的“已用时间”。您将无法看到延迟或字节指标,因为 JMeter 对您的 JUnit 测试一无所知。因此,JMeter 只能测量您的测试方法执行时间

    2. 目前(根据​​ JMeter 2.11)无法运行 JUnit 套件或夹具。只能执行单个测试用例,每个 JUnit Request Sampler 实例只能执行一个测试用例

    3. JUnit 测试设计存在某些限制。由于 JMeter 的多线程特性,测试开发人员需要使用线程安全的方法(即尽可能避免使用静态字段和方法,并记住资源可能正忙于另一个线程)。

    4. 有限的 JUnit 请求采样器配置功能假定所有测试前和测试后逻辑都应进入 setUp 和 tearDown 方法。如果您需要将外部数据提供给采样器,请使用属性文件或系统属性

    你最有可能受到第 3 点的影响。

    此外,根据WebDriver Tutorial

    注意:本项目无意替换 JMeter 中包含的 HTTP 采样器。相反,它旨在通过测量最终用户加载时间来赞美它们。

    另外,来自同一来源:

    根据经验,读者创建的浏览器(线程)数量应受以下公式限制:

    C = B + 1
    

    其中 C = 运行测试的主机的核心数 N = 浏览器数(线程)。 例如,如果当前阅读器的主机有 4 个内核,则公式将得出:

    4 = 3 + 1
    

    意味着脚本应该有 MAXIMUM 3 个线程。

    因此,我建议使用 JMeter 来产生主要负载,并使用 WebDriver Sampler 或通过 JUnit Sampler 在单独线程组中的 1 个线程中的现有代码来测量现实生活中的用户体验。

    【讨论】:

    • 非常感谢!第 3 点是我项目的真正问题。只是试图在我的项目中避免静态引用,它现在可以与多个线程一起使用。也感谢您的建议。它们非常有用。
    【解决方案2】:

    这个问题我有一段时间了,没什么,请声明你所有的变量没有“静态”关键字,它对我有用。

    谢谢

    【讨论】:

      【解决方案3】:

      正如 Dmitri T 在他的回答中指出的那样,当使用一些静态字段时,通过 Jmerter 运行 JUnit-Selenium 测试的多线程会中断。 在我们的项目中,我们遇到了与问题状态相同的症状,例如测试找不到元素,或者当其他人正在使用它时一个线程关闭了webdriver。

      因此我们实现了以下方法,这使我们的测试踏步安全并允许通过 JMeter 以多线程方式运行。

      首先,让我们提一下,Junit-Selenium 测试中的静态对象通常与@BeforeClass 方法一起出现,这些方法必须声明为静态并使用静态字段。您也可以使用@ClassRule 注释,该注释也必须放在静态字段上。不过@ClassRule注解并没有被JMeter Junit Sampler处理,所以我就不多说了。

      在我们的例子中使用@BeforeClass 初始化,我们有TestBase 类,我们在其中初始化webdriver 和一些其他资源以用于所有测试。我只是简化了我们的真实类以进行更清晰的演示:

       public abstract class P01_SL_TestBase {
          public static WebDriverResource driver = new FirefoxDriverResource();
          public static PageProvider<ARMLoginPage> loginPageProvider;
          public static ARMLoginPage loginPage;
          public static ARMApplicationStartPage ARMPage;
      
          @BeforeClass
           public static void loadApplication () {
              initResourcesForJmeterRunner();
              loadARM (armUser); 
           }
      
           public static void initResourcesForJmeterRunner() throws Throwable {
               webDriverResource.before();
           }
      
           public static void loadARM(BPMUser armUser) {
      
              loginPageProvider =
                  new PageProvider<ARMLoginPage>(ARMLoginPage.class,
                                                 BASE_URL_ADF_ARM_LOGIN_PAGE,
                                                 driver.getDriver()); //Here
                                                 //we get thread-local value of 
                                                 //Firefox driver for our current thread 
                                                 //from the threadwide storage
              loginPage = loginPageProvider.goHome();
      
          }
      }
      

      正如您在TestBase 中看到的,我们有静态字段驱动程序和其他一些常见情况。还有@BeforeClass 方法可以做两件事。首先,webdriver 被实例化。其次,初始化要测试的应用程序。所以TestBase 类中的所有内容都与在一个线程或多线程中运行的测试无关。

      多线程运行的实现我们放到WebDriverResource类中:

      public abstract class WebDriverResource extends ExternalResource {
      
          //  private final RemoteWebDriver driver; //this is implementation for 
                                                    //one thread running, 
                                                    //it is not thread-safety
      
          /**
           * {@code threadLocalDriverStorage} field stores thread-local instances
           * of {@code RemoteWebDriver}.
           * Note that now {@code threadLocalDriverStorage} is not static. We made
           * this for backward compatibility of some 
           * existent tests using non-static createDriver() method.
           * TODO refactor createDriver() declaration and implementation to static
           * variant.
           */
          private ThreadLocal<RemoteWebDriver> threadLocalDriverStorage = 
                       new ThreadLocal<RemoteWebDriver>() {
              @Override
              protected RemoteWebDriver initialValue() {
                  return createDriver(locale.toLanguageTag());
              }
          };
      
          public WebDriverResource() {
          }
      
          protected abstract RemoteWebDriver createDriver(String language);
      
          @Override
          public void before() throws Throwable {
              //here we call threadLocalDriverStorage.get() first time,
              //and so the get() will perform initialValue() 
              //and save to storage thread-local value for the current thread
              threadLocalDriverStorage.get()  
                               .manage()     
                               .window()
                               .maximize();    
          }
      
          @Override
          protected void after() {
              logger.fine("quit browser...");
              //driver.quit();
              threadLocalDriverStorage.get().close();
          }
      
          /**
           * @return thread-local value of {@code RemoteWebDriver}
           */
          public RemoteWebDriver getDriver() {
              return threadLocalDriverStorage.get();
          }
      }
      

      这里我们使用TreadLocal类来实现threadLocalDriverStorage字段。我们重写initialValue() 方法以返回RemoteWebDriver 对象的实例。 然后在before() 方法中,当我们第一次调用TreadLocal.get() 时,它会导致调用initialValue() 方法并为当前线程实例化RemoteWebDriver 对象。 还有getDriver(),它返回胎面局部值RemoteWebDriver。应该在所有测试类中使用此方法来获取驱动程序对象。

      我们还将 webdriver 字段存储在页面对象模型类中。因此,我们还将受保护的静态 ThreadLocal&lt;RemoteWebDriver&gt; threadLocalDriverStorage 字段添加到我们的基本页面类中。但是这里我们不覆盖initialValue() 方法。相反,我们使用TreadLocal.set() 方法来保存当前线程的值。

      public abstract class Page {
      
          //  private final RemoteWebDriver driver; //this is implementation for
                                                    //one thread running,
                                                    //is not thread-safety
      
          /**
           * {@threadLocalDriverStorage} field stores thread-local instances of
           * {@RemoteWebDriver}.
           * 
           * Note that {@threadLocalDriverStorage} must be static. Without that
           * static classes extended Page will 
           * work with unpredictable instance of {@threadLocalDriverStorage} and
           * as result would not set and get {@RemoteWebDriver} instance properly 
           * 
           * Note that {@ThreadLocal.initialValue()} method is not overrided.
           * Therefore thread-local value must be stored by
           * {@ThreadLocal.set()} method.
           * We do that in class consractor.
           */
          protected static ThreadLocal<RemoteWebDriver> threadLocalDriverStorage = 
                                 new ThreadLocal<RemoteWebDriver>();
      
          public Page(WebDriver driver) {
              super();
              threadLocalDriverStorage.set((RemoteWebDriver)driver);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2023-03-29
        • 2017-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多