pokeahontas
pokeahontas

Reputation: 37

TestNG Appium Parallel execution not working

I'm trying to run automated tests in parallel via TestNGCucumberRunner and my problem is that I instantiate my PageObjects with a static get method from my Manager class, as the driver inside it is static. I'm pretty sure this is the problem for the parallel execution. How do I retrieve the driver in a thread-safe way?

Any help is appreciated.

Edit: My current driver class:

public class LocalDriverManager {
    private static ThreadLocal<AppiumDriver> localDriver = new ThreadLocal<>();

    public static AppiumDriver getDriver() {
        return localDriver.get();
    }

    static void setDriver(AppiumDriver driver) {
        localDriver.set(driver);
    }

    public static ThreadLocal<AppiumDriver> getLocalDriver() {
        return localDriver;
    }
}

Here I initialize the driver in another class:

This is how I initialize driver and set ThreadLocal in another class:

if (capabilities.getPlatform().is(Platform.IOS)) {
            driver = new IOSDriver<MobileElement>(new URL(urlStart + appiumServer.getPort() + "/wd/hub"),
                    capabilities);
        } else {
            driver = new AndroidDriver<MobileElement>(
                    new URL(urlStart + appiumServer.getPort() + "/wd/hub"),
                    capabilities);
        }
        LocalDriverManager.setDriver(driver);

I set the localDriver variable when I set my driver in another class and at

public BasePage(AppiumDriver driver, WebDriverWait wait) {
        this.driver = driver;
        this.wait = wait;
        PageFactory.initElements(new AppiumFieldDecorator(driver, Duration.ofSeconds(10)), this);
    }

I receive this exception now:

 java.lang.IllegalArgumentException: Can not set io.appium.java_client.MobileElement field ba.woop.automation.pages.LandingPage.itsBanking to org.openqa.selenium.remote.RemoteWebElement$$EnhancerByCGLIB$$d27c0df4
        at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
        at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
        at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
        at java.base/java.lang.reflect.Field.set(Field.java:780)
        at org.openqa.selenium.support.PageFactory.proxyFields(PageFactory.java:117)
        at org.openqa.selenium.support.PageFactory.initElements(PageFactory.java:105)
        at ba.woop.automation.BasePage.<init>(BasePage.java:35)
        at ba.woop.automation.pages.LandingPage.<init>(LandingPage.java:30)
        at ba.klika.automation.steps.LoginCheckSteps.<init>(LoginCheckSteps.java:13)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at org.picocontainer.injectors.AbstractInjector.newInstance(AbstractInjector.java:145)
        at org.picocontainer.injectors.ConstructorInjector$1.run(ConstructorInjector.java:342)
        at org.picocontainer.injectors.AbstractInjector$ThreadLocalCyclicDependencyGuard.observe(AbstractInjector.java:270)
        at org.picocontainer.injectors.ConstructorInjector.getComponentInstance(ConstructorInjector.java:364)
        at org.picocontainer.injectors.AbstractInjectionFactory$LifecycleAdapter.getComponentInstance(AbstractInjectionFactory.java:56)
        at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:64)
        at org.picocontainer.behaviors.Stored.getComponentInstance(Stored.java:91)
        at org.picocontainer.DefaultPicoContainer.getInstance(DefaultPicoContainer.java:699)
        at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:647)
        at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:678)
        at io.cucumber.picocontainer.PicoFactory.getInstance(PicoFactory.java:42)
        at cucumber.runtime.java.ObjectFactoryLoader$ObjectFactoryAdapter.getInstance(ObjectFactoryLoader.java:157)
        at cucumber.runtime.java.JavaStepDefinition.execute(JavaStepDefinition.java:57)
        at cucumber.runner.PickleStepDefinitionMatch.runStep(PickleStepDefinitionMatch.java:50)
        at cucumber.runner.TestStep.executeStep(TestStep.java:65)
        at cucumber.runner.TestStep.run(TestStep.java:50)
        at cucumber.runner.PickleStepTestStep.run(PickleStepTestStep.java:43)
        at cucumber.runner.TestCase.run(TestCase.java:46)
        at cucumber.runner.Runner.runPickle(Runner.java:50)
        at io.cucumber.testng.TestNGCucumberRunner.runScenario(TestNGCucumberRunner.java:79)
        at ba.woop.automation.runners.LoginCheckRunner.runScenario(LoginCheckRunner.java:36)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
        at org.testng.internal.Invoker.invokeMethod(Invoker.java:583)
        at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719)
        at org.testng.internal.TestMethodWithDataProviderMethodWorker.call(TestMethodWithDataProviderMethodWorker.java:71)
        at org.testng.internal.TestMethodWithDataProviderMethodWorker.call(TestMethodWithDataProviderMethodWorker.java:14)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:834)
        at ?.the user clicks on LOGIN button on LANDING PAGE(file:src/test/resources/features/loginCheck.feature:3)

Upvotes: 0

Views: 825

Answers (1)

Vault23
Vault23

Reputation: 753

You need to create particular class to store your 'drivers' per thread.

public class ThreadLocalDriver {

    private static ThreadLocal<AppiumDriver> tlDriver = new ThreadLocal<>();

    public static ThreadLocal<AppiumDriver> getTlDriver() {
        return tlDriver;
    }

    public static void setTLDriver(AppiumDriver driver) {
            tlDriver.set(driver);
    }
}

Once you need to get your driver, just call static method ThreadLocalDriver.getTlDriver().get();

Upvotes: 1

Related Questions