使用Java Sampler和多个线程在Jmeter中运行时,Selenium测试不起作用

时间:2015-03-17 17:03:08

标签: multithreading selenium jmeter

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

3 个答案:

答案 0 :(得分:1)

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

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

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

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

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

  7.   

你最有可能患上第3点。

此外,根据WebDriver Tutorial

  

注意:此项目的目的不是替换JMeter中包含的HTTP Samplers。相反,它意味着通过测量最终用户加载时间来补充它们。

此外,来自同一来源:

  

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

C = B + 1
  

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

4 = 3 + 1
  

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

因此我建议使用JMeter通过单独线程组中的1个线程中的JUnit Sampler生成主加载和WebDriver Sampler或现有代码,以测量实际用户体验。

答案 1 :(得分:0)

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

由于

答案 2 :(得分:0)

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

因此,我们实现了以下方法,该方法使我们的测试具有胎面安全性,并允许通过JMeter以多线程方式运行runnig。

首先,让我们提到Junit-Selenium测试中的静态对象通常使用@BeforeClass方法出现,这些方法必须声明为静态并且可以使用静态字段。同样,您可能会使用@ClassRule批注,该批注也必须放在静态字段中。但是,JMeter Junit Sampler不会处理@ClassRule注释,因此我将不再赘述。

在我们的案例中,通过@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<RemoteWebDriver> 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);
    }
}