0
votes

I'm running Selenium tests for Web site using Jmeter and Java Sampler. I've exported test class which extends AbstractJavaSamplerClient using "Eclipse ->Export to jar" and copy it to the Jmeter/lib/ext. I'm using classes from another java project in my test. I packed this project to .jar and copy it to Jmeter/lib. When I run my test in Jmeter in 1 thread it works just fine but if I run 2 or more threads Selenium web driver fails to find elements but I see that they are visible. I'm new to it but looks like Selenium cannot work in multiple threads. What am I doing wrong? Please help.

3

3 Answers

1
votes

As per How to Use JUnit With JMeter guide certain restrictions apply to JMeter's JUnit Samplers, to wit

  1. Only the “Elapsed Time” is recorded for the JUnit Request Sampler. You won’t be able to see Latency or Bytes metrics as JMeter knows nothing about your JUnit test. Therefore, JMeter is only capable of measuring your test method execution time

  2. There is currently (as per JMeter 2.11) no way to run the JUnit suite or fixture. Only individual test cases can be executed and only one test case per the JUnit Request Sampler instance

  3. There are certain limitations to the JUnit test design. Due to JMeter’s multithreaded nature, the test developer needs to use a thread-safe approach (i.e. avoid static fields and methods wherever possible and keep in mind that the resource might be busy with another thread).

  4. Limited JUnit Request Sampler configuration capabilities assume that all pre- and post-test logic should go in setUp and tearDown methods. If you need to feed external data to a sampler use property files or system properties

You're most likely to be suffering from point 3.

Moreover, as per WebDriver Tutorial

Note: It is NOT the intention of this project to replace the HTTP Samplers included in JMeter. Rather it is meant to compliment them by measuring the end user load time.

In addition, from the same source:

From experience, the number of browser (threads) that the reader creates should be limited by the following formula:

C = B + 1

where C = Number of Cores of the host running the test and N = Number of Browser (threads). eg, if the current reader's host has 4 cores, the formula would yield:

4 = 3 + 1

meaning that the script should have a MAXIMUM of 3 threads.

So I would suggest to use JMeter to produce the main load and either WebDriver Sampler or your existing code via JUnit Sampler in 1 thread in a separate thread group to measure real life user experience.

0
votes

I was with this problem for some time, its nothing, please declare all your variables without "static" key word, its worked for me.

Thanks

0
votes

As Dmitri T point in his answer the multitread running of JUnit-Selenium tests through Jmerter brokes when some static fields are used in. In our project we faced with the same sympthoms as question states, like test can not find element, or one thread close webdriver when others are using it.

So we implement following approach which makes our test tread-safety and allows runnig in multithread way through JMeter.

First, let's mention that static objects in Junit-Selenium tests usually appear with @BeforeClass methods which must be declared static and work with static fields. Also your probably could use @ClassRule annotation which also must be put on the static field. However @ClassRule annotation is not processed by JMeter Junit Sampler, so I will not talk about it further.

With @BeforeClass initialization in our case we have TestBase class where we initialize webdriver and some other resources to use over all tests. I just simplify our real classes for more clear demonstration:

 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();

    }
}

As you can see in TestBase we have common case with static field driver and some others. Also there is @BeforeClass method which do two things. First, webdriver is instantinated. Second, the application to be tested is initialized. So everything in TestBase class is indifferent to the running tests in one thread or in multitread.

The implemantation of multithread running we put into WebDriverResource class:

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();
    }
}

Here we use TreadLocal class to implement threadLocalDriverStorage field. We override initialValue() method to return instance of RemoteWebDriver object. Then in before() method when we call TreadLocal.get() for the first time it leads calling initialValue() method and instantinating the RemoteWebDriver object for current thread. Also there is getDriver() which return tread-local value of RemoteWebDriver. This method should be used all over the test classes to get driver object.

As well we store webdriver fields in our page object model classes. So, we also add protected static ThreadLocal<RemoteWebDriver> threadLocalDriverStorage field into our base page class. But here we do not override initialValue() method. Instead, we use TreadLocal.set() method to save value for current thread.

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