Single Test for IOS and Android (Parallel Testing) Don't Work

Hello everyone,

I’ve been working on an Appium project to learn how to run tests on both iOS and Android devices. I’m using Appium, Selenium, JUnit, and Cucumber for test automation.

But I’m facing a problem when I run my tests with “mvn test” command. On Android, everything works fine, but on iOS, the app just opens and doesn’t progress with the test.

To make it easier for readability, I’ve divided my project code into three parts, which I’m sharing below. Additionally, I should mention that I have two Appium servers running on different ports, which might be relevant to the issue.

Your help with this would be greatly appreciated. Thank you!

AppiumDriverFactory.java

package utils;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;

import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;

public class AppiumDriverFactory {

    private static AndroidDriver androidDriver;
    private static IOSDriver iOSDriver;

    @BeforeMethod
    public static AndroidDriver getAndroidDriver() throws MalformedURLException {
        if (androidDriver == null) {
            // Setup Driver and Device
            URL appiumURL = new URL("http://127.0.0.1:4723");
            UiAutomator2Options options = getAndroidOptions();
            androidDriver = new AndroidDriver(appiumURL, options);
        }
        return androidDriver;
    }

    @BeforeMethod
    public static IOSDriver getIOSDriver() throws MalformedURLException, InterruptedException {
        if (iOSDriver == null) {
            // Setup Driver and Device
            URL appiumURL = new URL("http://127.0.0.1:4733");
            XCUITestOptions options = getIOSOptions();
            iOSDriver = new IOSDriver(appiumURL, options);
        }
        return iOSDriver;
    }


    private static XCUITestOptions getIOSOptions() throws InterruptedException {
        XCUITestOptions options = new XCUITestOptions();
        options.setDeviceName("iPhone 15 Pro");
        options.setApp("//Users//user//Desktop//Personal//AppiumCucumberBDD//src//test//resources//Apps//iOS.Simulator.SauceLabs.Mobile.Sample.app.2.7.1.app");
        options.setPlatformVersion("17.0");
        options.setWdaLocalPort(8100);
        options.setUdid("9DD824E4-2E7A-439E-BF04-E95B6505BE8C");
        options.setWdaLaunchTimeout(Duration.ofSeconds(10));
        Thread.sleep(10000);
        return options;
    }

    private static UiAutomator2Options getAndroidOptions() {
        UiAutomator2Options options = new UiAutomator2Options();
        options.setDeviceName("MertsEmulator");
        options.setApp("//Users//user//Desktop//Personal//AppiumCucumberBDD//src//test//resources//Apps//Android.SauceLabs.Mobile.Sample.app.2.7.1.apk");
        options.setAppWaitActivity("com.swaglabsmobileapp.MainActivity");
        options.setChromedriverExecutable("//Users//user//Desktop//Personal//AppiumBDD//src//test//resources//Driver//chromedriver");
        options.setSystemPort(8001);
        options.setUdid("emulator-5554");
        return options;
    }

    @AfterTest
    public void tearDownAndroid() {
        if (androidDriver != null) {
            androidDriver.quit();
        }
    }

    @AfterTest
    public  void tearDowniOS() {
        if (iOSDriver != null) {
            iOSDriver.quit();
        }
    }
}

LoginPageObject.java
package iosApp.pages;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.pagefactory.*;
import org.junit.Assert;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.time.Duration;

public class LoginPageObject {
    private IOSDriver iosDriver;
    private AndroidDriver androidDriver;

    @iOSXCUITFindBy(accessibility = "test-LOGIN")
    @AndroidFindBy(accessibility = "test-Login")
    private WebElement loginModal;
    public LoginPageObject(IOSDriver iosDriver, AndroidDriver androidDriver) {
        this.androidDriver = androidDriver;
        this.iosDriver = iosDriver;
        PageFactory.initElements(new AppiumFieldDecorator(iosDriver), this);
        PageFactory.initElements(new AppiumFieldDecorator(androidDriver), this);
        //waitIOSElementExistence(loginModal);
        //waitAndroidElementExistence(loginModal);
    }

    @iOSXCUITFindBys(value = {@iOSXCUITBy(accessibility = "test-Username"), @iOSXCUITBy(xpath = "//XCUIElementTypeTextField[@name=\"test-Username\"]")})
    @AndroidFindBy(accessibility = "test-Username")
    private WebElement Username;

    @iOSXCUITFindBy(accessibility = "test-Password")
    @AndroidFindBy(accessibility = "test-Password")
    private WebElement Password;

    @AndroidFindBy(accessibility = "test-LOGIN")
    @iOSXCUITFindBy(accessibility = "test-LOGIN")
    private WebElement LoginButton;

    public void inputValidCredentials(){
        Username.sendKeys("standard_user");
        Password.sendKeys("secret_sauce");
        LoginButton.click();
    }

}

LoginStepDef.java
package iosApp.stepdefs;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.ios.IOSDriver;
import io.cucumber.java.en.Given;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import iosApp.pages.LoginPageObject;
import utils.AppiumDriverFactory;

import java.net.MalformedURLException;

public class LoginStepDef {

    private IOSDriver iosDriver;
    private AndroidDriver androidDriver;
    private LoginPageObject loginPageObject;

    public LoginStepDef() throws InterruptedException, MalformedURLException {
        this.iosDriver = AppiumDriverFactory.getIOSDriver();
        this.androidDriver= AppiumDriverFactory.getAndroidDriver();
        loginPageObject =  new LoginPageObject(iosDriver,androidDriver);
    }

    @Given("I go to the Login Page")
    public void iGoToTheLoginPage() {
        //
    }

    @When("I log in with valid credentials")
    public void iLogInWithValidCredentials() {
        loginPageObject.inputValidCredentials();
    }

    @Then("I should be in the products list page")
    public void iShouldBeInTheProductsListPage() {
        //
    }
}

Maybe you can enlighten me ?
@Aleksei
@ Aleksei

check https://appiumpro.com/editions/28-running-multiple-appium-tests-in-parallel