2

Using Browserstack tutorials (https://www.browserstack.com/app-automate/appium-junit) and sample project (https://github.com/browserstack/junit-appium-app-browserstack) I am struggling with setup of parallel tests.

Specifically, I need to run suirte with Cucumber.class (@RunWith(Cucumber.class)) for my tests to be read from scenarios, while Browserstack documentation tells me to run with Parameterized.class (public class Parallelized extends Parameterized). The biggest problem I encounter is how to parse multiple device+os configurations to Browserstack, if you run the suite with Cucumber class.

My Runner class:

package step_definitions;

import org.junit.runner.RunWith;
import io.cucumber.junit.CucumberOptions;
import io.cucumber.junit.Cucumber;

@RunWith(Cucumber.class)
@CucumberOptions(features = {
        "src/main/resources/FeatureFiles" }, dryRun = false, strict = false, monochrome = true, plugin = {
                "html:target/cucumber", "json:target/cucumber.json" },
        // glue = {"iOSAutomation/src/test/java/step_definitions"},

        tags = { "@Login"})

public class RunTest {

}

Launcher:

package step_definitions;

import (...)

public class Launcher {

    public static IOSDriver<IOSElement> driver;
    public static WebDriverWait wait;

    // Parallel BS tests
    private static JSONObject config;

    @Parameter(value = 0)
    public static int taskID;

    @Parameters
    public static Iterable<? extends Object> data() throws Exception {
        List<Integer> taskIDs = new ArrayList<Integer>();

        if (System.getProperty("config") != null) {
            JSONParser parser = new JSONParser();
            config = (JSONObject) parser.parse(new FileReader("src/main/resources/conf/" + System.getProperty("config")));
            int envs = ((JSONArray) config.get("environments")).size();

            for (int i = 0; i < envs; i++) {
                taskIDs.add(i);
            }
        }       

        return taskIDs;
    }

    @Before

    public static void Launchapp(Scenario scenario) throws InterruptedException, FileNotFoundException, IOException, ParseException {

        JSONArray envs = (JSONArray) config.get("environments");

        DesiredCapabilities caps = new DesiredCapabilities();

            caps.setCapability(MobileCapabilityType.AUTOMATION_NAME, "xcuitest");
            caps.setCapability(MobileCapabilityType.PLATFORM_NAME, "iOS");
            caps.setCapability("bundleId", bundleId);
            caps.setCapability(MobileCapabilityType.APP, "useNewWDA");
            caps.setCapability(MobileCapabilityType.APP, "clearSystemFiles");
            caps.setCapability(MobileCapabilityType.APP, app);
            caps.setCapability("browserstack.local", "false");
            caps.setCapability("webkitResponseTimeout", "60000");
            caps.setCapability("browserstack.localIdentifier", "Test123");
            caps.setCapability("browserstack.appium_version", "1.9.1");
            caps.setCapability("startIWDP", true);
            caps.setCapability("instrumentApp", true);
            caps.setCapability("webkitResponseTimeout", 70000);

            Map<String, String> envCapabilities = (Map<String, String>) envs.get(taskID);
            Iterator it = envCapabilities.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry pair = (Map.Entry) it.next();
                caps.setCapability(pair.getKey().toString(), pair.getValue().toString());
            }

            driver = new IOSDriver<IOSElement>(
                    new URL("http://" + userName + ":" + accessKey + "@hub-cloud.browserstack.com/wd/hub"), caps);

            sessionId = driver.getSessionId().toString();


        driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
        wait = new WebDriverWait(driver, 30);

    }

    @After
    public void tearDown(Scenario scenario) throws Exception {
        driver.quit();
    }


}

Parallelized.java:

package step_definitions;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.junit.runners.Parameterized;
import org.junit.runners.model.RunnerScheduler;

import io.cucumber.junit.Cucumber;

public class Parallelized extends Parameterized {

  private static class ThreadPoolScheduler implements RunnerScheduler {
    private ExecutorService executor; 

    public ThreadPoolScheduler() {
      String threads = System.getProperty("junit.parallel.threads", "4");
      int numThreads = Integer.parseInt(threads);
      executor = Executors.newFixedThreadPool(numThreads);
    }

    @Override
    public void finished() {
      executor.shutdown();
      try {
        executor.awaitTermination(10, TimeUnit.MINUTES);
      } catch (InterruptedException exc) {
        throw new RuntimeException(exc);
      }
    }

    @Override
    public void schedule(Runnable childStatement) {
      executor.submit(childStatement);
    }
  }

  public Parallelized(Class klass) throws Throwable {
    super(klass);
    setScheduler(new ThreadPoolScheduler());
  }
}

and config file:

{
  "environments": [{
    "device": "iPhone XR",
    "os_version": "12"
  }, {
    "device": "iPhone 6S",
    "os_version": "11"
  }, {
    "device": "iPhone XS",
    "os_version": "13"
  }, {
    "device": "iPhone XS Max",
    "os_version": "12"
  }]
}

How to make it work? May I run this with Cucumber.class AND somehow incorporate methods from Parallelized.java?

Rado
  • 41
  • 5
  • When running in parallel `static` variables are shared between threads. This means that different scenarios try to do different things with your drivers. You should use DI (e.g. cucumber-picocontainer`) to share state between your steps. – M.P. Korstanje Sep 26 '19 at 12:58
  • Thanks for pointing that out! I'll rework this. But it doesn't really takes me closer to the solution? – Rado Sep 26 '19 at 13:33
  • do you really have to use Junit? i would recommend you to use TestNG with the same frameworks, it works fine. It has parallelism, ability to work with cucumber and pretty clear configuration – Vault23 Sep 26 '19 at 13:58
  • I know it's far easier with TestNG, on the other hand it's not best approach to switch technology every time you find an issue. That would be the last thing I'll do if not find a better way. – Rado Sep 26 '19 at 15:47

1 Answers1

0

You can use MakeFile where you can provide all the devices or platform capabilities and with cucumber-jvm-parallel-plugin the tests can be run parallel in Browserstack. This will be the easiest solution.

Sample MakeFile:

browserstack_parallel: make -j bs_iPhoneXS bs_iPhoneX

bs_iPhoneXS: mvn test -Dbs_local_testing=false -Dbs_device=iPhoneXS -Dbs_app=bs://0fb247cde17a979db4d7e5a521bc600af7620b63

bs_iPhoneX: mvn test -Dbs_local_testing=false -Dbs_device=iPhoneX -Dbs_app=bs://0fb247cde17a979db4d7e5a521bc600af7620b63

You can run MakeFile from terminal by typing make browserstack_parallel

Sooraj
  • 565
  • 3
  • 8