Параллельный запуск тестов appium + jnit + grid, как указать несколько девайсов для прогона тестов?

  1. Запустил hub c selenium grid.
  2. Запустил два экземпляра appium, запустил два эмулятора Genimotion
  3. В консоли грида вижу оба эмулятора.
    Запускаю тесты.
    Ожидаю: Тесты будут прогоняться на двух девайсах
    Реально: Тесты запускаются на одном девайсе.
    Вопрос:
    Как правильно передать в capabilities несколько устройств чтобы все тесты прогонялись на всех подключенных девайсах?

Вам следует гдето задать количество тредов для junit. В тестНГ есть возможность указывать количество тредов, возможно в jUnit стото подобное есть. Так что это не проблема капабилитис аппиума, это сам junit нужно копать

В pom файле в настройках maven-surefire-plugin я указал:
<parallel>methods</parallel>
<threadCount>2</threadCount>

Но этого видимо мало.

Можно увидеть ваш pom.xml ?

<?xml version="1.0" encoding="UTF-8"?>


4.0.0

<groupId>ru.company</groupId>
<artifactId>project-android</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java-language.level>1.8</java-language.level>

    <io.appium.version>6.1.0</io.appium.version>
    <junit.version>4.12</junit.version>

    <properties.loader.version>1.6</properties.loader.version>

    <allure.junit.adaptor.version>1.5.0.RC2</allure.junit.adaptor.version>
    <allure.maven.plugin.version>2.5</allure.maven.plugin.version>
    <aspectj.version>1.8.11</aspectj.version>

    <log4j.version>2.11.2</log4j.version>

    <gson.version>2.8.0</gson.version>

    <assertj.version>3.11.1</assertj.version>

    <maven-shade-plugin.version>2.4.3</maven-shade-plugin.version>
    <maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version>
    <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>

</properties>

<repositories>
    <repository>
        <id>company-maven</id>
        <url>http://url.ru</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>io.appium</groupId>
        <artifactId>java-client</artifactId>
        <version>${io.appium.version}</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
    </dependency>

    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>${gson.version}</version>
    </dependency>

    <dependency>
        <groupId>ru.yandex.qatools.properties</groupId>
        <artifactId>properties-loader</artifactId>
        <version>${properties.loader.version}</version>
    </dependency>

    <dependency>
        <groupId>ru.yandex.qatools.allure</groupId>
        <artifactId>allure-junit-adaptor</artifactId>
        <version>${allure.junit.adaptor.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>${log4j.version}</version>
    </dependency>

    <dependency>
        <groupId>org.assertj</groupId>
        <artifactId>assertj-core</artifactId>
        <version>${assertj.version}</version>
        <scope>test</scope>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>${maven-surefire-plugin.version}</version>
            <configuration>
                <parallel>methods</parallel>
                <threadCount>2</threadCount>
                <runOrder>alphabetical</runOrder>
                <testFailureIgnore>false</testFailureIgnore>
                <argLine>
                    -Dfile.encoding=UTF-8
                    -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                </argLine>
                <properties>
                    <property>
                        <name>listener</name>
                        <value>ru.yandex.qatools.allure.junit.AllureRunListener</value>
                    </property>
                </properties>
            </configuration>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjweaver</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <source>${java-language.level}</source>
                <target>${java-language.level}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

Также в консоли грида вижу:
3 requests waiting for a slot to be free.
и указаны Capabilities для второго девайса, но он не используется совсем, весь прогон идет на первом девайсе

Тесты запускаю через команду
mvn -T 2 test -Dtest=TestClass

А как вы сейчас задаете капабилитис для аппиум драйвера ? И как стартуете драйвер? Можно увидеть код ?

Сейчас пробую аппиумы из консоли запускать сразу передавая им device-name:
appium --nodeconfig C:\selenium-nodes\nodes_config_device_1.json -p 4723 --device-name 1.1.1.1:5555
appium --nodeconfig C:\selenium-nodes\nodes_config_device_2.json -p 4724 --device-name 1.1.1.2:5555
(тут значение device-name для примера)

В коде пока не знаю как задать чтобы несколько девайсов передавать в deviceName.
Создание драйвера:

public class DriverProvider {
private SettingsPropLoader settingsLoader = new SettingsPropLoader();

public AndroidDriver prepareDriver(boolean withReset) {
return prepare(withReset);
}

private AndroidDriver prepare(boolean withReset) {
AndroidDriver driver = null;
DesiredCapabilities capabilities = new DesiredCapabilities();

if (!withReset) {
capabilities.setCapability(“noReset”, true);
capabilities.setCapability(“fullReset”, false);
}

String gridUrl = “http://0.0.0.0:4444/wd/hub”;

capabilities.setCapability(“app”, settingsLoader.getTestAppPath());
capabilities.setCapability(“appPackage”, settingsLoader.getAppPackage());
capabilities.setCapability(“appActivity”, settingsLoader.getAppActivity());
capabilities.setCapability(“automationName”, settingsLoader.getAutomationName());
capabilities.setCapability(“platformName”, settingsLoader.getPlatformName());

try {
driver = new AndroidDriver(new URL(gridUrl), capabilities);
} catch (MalformedURLException e) {
e.printStackTrace();
}

return driver;
}
}

Базовый класс:
public class BaseTest {
protected static AndroidDriver driver;

@Before
public void setUp() {
launchApp(true);
}

protected void launchApp(boolean withReset) {
driver = new DriverProvider().prepareDriver(withReset);
}

@After
public void tearDown() {
driver.quit();
}
}

Хмм… пока единственное что могу сказать что если вы хотите в паралелизацию то статик для драйвера нужно убирать

protected static AndroidDriver driver;

Возможно в этом кстати и кроется проблема. У вас, к примеру, 2 теста, а в случае статик static AndroidDriver driver драйвер один, т.е. оба теста ссылаются на один драйвер.

Также для пробега тестов в нескольких тредах, драйвер вообще должен быть ThreadLocal

посмотрите сдесь неплохо описано
https://www.swtestacademy.com/appium-parallel-tests/

Все сделал, но пока результат тот же.

Если идейно только Grid - добавьте в строку запуска UDID каждого девайса. Кроме того для гибридных приложений нужно указать 3 порта (android server, native agent, web agent).
пример: appium -a localhost -p 4730 -bp 4720 --chromedriver-port 8400 --nodeconfig /path/to/device/config.json --default-capabilities ‘{“udid”:“DSFasfs32434”}’
Для каждого инстанса appium server p/bp/chromedriver-port/nodeconfig/default-capabilities должы быть уникальнийми (порты не заняты другими сервисами).
Если не указать udid - каждый appium server возьмет первый девайс/емулятор в списке от adb devices.
На своем проекте я использую GitHub - ochubey/device-dealer: Device management service that helps you to run Appium tests in parallel with real devices in your local lab.. Там автоматизирована вся рутина, а мне будет приятно если решение поможет не только мне :wink:

Спасибо вам.
Моя проблема была в том что думал device-name достаточно указать, но udid был и правда необходим.
Запускаю аппиум так:

appium --nodeconfig C:\selenium-nodes\nodes_config_device_1.json -p 4723 --device-name Device_1 --udid 1.1.1.1:5555
appium --nodeconfig C:\selenium-nodes\nodes_config_device_2.json -p 4724 --device-name Device_2 --udid 1.1.1.2:5555

и все заработало.
Мне бы конечно параметры device-name и udid куда нибудь в файл вынести и оттуда читать…
То есть прямо в коде где я задаю

capabilities.setCapability(“app”, settingsLoader.getTestAppPath());
capabilities.setCapability(“appPackage”, settingsLoader.getAppPackage());
capabilities.setCapability(“appActivity”, settingsLoader.getAppActivity());
capabilities.setCapability(“automationName”, settingsLoader.getAutomationName());
capabilities.setCapability(“platformName”, settingsLoader.getPlatformName());

но не могу понять как подавать например в deviceName, так как там два значения, в отличие например от platformName

1 лайк

Вам обязательно контролировать какой тест на каком девайсе запускать?
Если нет (тоесть тест не зависит от названия девайса/версии) то есть следующий трюк:

{
  "capabilities": [
    {
      "applicationName": "android",
      "platformName": "android",
      "maxInstances": 1,
      "udid": "0815f85ce2103703",
      "platformVersion": "5.1.1",
      "platform": "MAC"
    }
  ],
  "configuration": {
    "cleanUpCycle": 2000,
    "host": "127.0.0.1",
    "hubHost": "127.0.0.1",
    "hubPort": 4444,
    "maxSession": 1,
    "port": 53251,
    "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
    "register": true,
    "registerCycle": 5000,
    "timeout": 30000
  }
}

А в капабилитях добавить
capabilities.setCapability(“applicationName”, “android”);
capabilities.setCapability(“deviceName”, “android”);
UPD: ошибка на скринах, обновил JSON