Run tests when device Slots are limited

Below you can find information on how to organize test execution in case of limited device Slots.

Prerequisites

  • Existing Group of tests or a Suite.
  • List of devices, where these tests should be executed.
  • Plan with limited device Slots for the Test Automation.

Set the number of threads in the Tests Project configuration

The number of threads in your tests should be related to the number of available Slots. In this case, avoiding issues with concurrent device usage is possible.
Below you can find examples for the most popular Java build tools.

Maven:

<!-- TestNG or JUnit4 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin-version}</version>
                <configuration>
                    <forkCount>0</forkCount>
                    <threadCount>${NUMBER_OF_SLOTS}</threadCount>
                    <parallel>classes</parallel>
                    <suiteXmlFiles>
                        ...
                    </suiteXmlFiles>
                    <argLine>
                        ....
                    </argLine>
                </configuration>
                <dependencies>
                    ...
                </dependencies>
            </plugin>
            
            
<!-- JUnit 5 -->            
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin-version}</version>
                <configuration>
                    <forkCount>1</forkCount>
                    <reuseForks>true</reuseForks>
                    <argLine>
                        ...
                    </argLine>
                    <properties>
                        <configurationParameters>
                            junit.jupiter.extensions.autodetection.enabled = true
                            junit.jupiter.testinstance.lifecycle.default = per_class
                            junit.jupiter.execution.parallel.enabled = true
                            junit.jupiter.execution.parallel.mode.default = same_thread
                            junit.jupiter.execution.parallel.mode.classes.default = concurrent
                            junit.jupiter.execution.parallel.config.strategy = fixed
                            junit.jupiter.execution.parallel.config.fixed.parallelism = ${NUMBER_OF_SLOTS}
                            junit.jupiter.execution.parallel.config.fixed.max-pool-size = ${NUMBER_OF_SLOTS}
                        </configurationParameters>
                    </properties>
                    <systemPropertyVariables>
                       ...
                    </systemPropertyVariables>
                </configuration>
                <dependencies>
                    ...
                </dependencies>
            </plugin>    

Gradle:

//TestNg
test {
    useTestNG {
        exclude 'integrationTests'
        include 'unitTests'
        maxParallelForks <NUMBER_OF_SLOTS>
    }
}

//JUnit
test {
    useJUnit {
        includeCategories 'org.gradle.junit.CategoryA'
        excludeCategories 'org.gradle.junit.CategoryB'
        maxParallelForks <NUMBER_OF_SLOTS>
    }
}

Parametrize the device UDID

To ensure that tests will be executed on a particular device, it’s better to parametrize the device UDID in a Class with tests or a Method.
The parameter values can be specified in a Suite file or a Source method.
Below you can find examples for the most popular Java testing frameworks.

TestNG:

<!--Suite File-->

<suite name="Smoke Native Automation Suite">
    <test name="Smoke tests Samsung">
        <parameter name="deviceUdid" value="<SAMSUNG_UDID>"></parameter>
        <classes>
            <class name="com.mobitru.tests.SmokeNativeTests"/>
        </classes>
    </test>
    <test name="Smoke tests Pixel">
        <parameter name="deviceUdid" value="<PIXELUDID>"></parameter>
        <classes>
            <class name="com.mobitru.tests.SmokeNativeTests"/>
        </classes>
    </test>
</suite>
//Class with tests

package com.mobitru.service;

import org.testng.annotations.*;

public class SmokeNativeTests {


    @Parameters("deviceUdid")
    @BeforeClass
    public void startDriver(String deviceUdid)  {

    }


}

JUnit 5:

package com.mobitru;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;


public class SmokeNativeTests {


    public static Stream<Arguments> deviceUdids() {
        return Stream.of(
                Arguments.of(<SAMSUNG_UDID>),
                Arguments.of(<PIXEL_UDID>)
        );
    }

    @ParameterizedTest
    @MethodSource("deviceUdids")
    void sanityTests(String deviceUdid) {
        
    }

}

Take the Device before the tests execution

The Device should be taken by API before start of the tests execution.
The provided UDID should be used in this API request.
Below you can find an example for a popular Java rest API library.

package com.mobitru.tests;

import com.mobitru.conf.GlobalConfig;
import io.restassured.RestAssured;
import org.aeonbits.owner.ConfigCache;

import static java.net.HttpURLConnection.HTTP_OK;

public class DeviceService {


    private final GlobalConfig config;

    public DeviceService() {
        this.config = ConfigCache.getOrCreate(GlobalConfig.class);
    }

    public void takeDeviceById(String udid) {
        RestAssured.
                given().
                baseUri(config.apiUrl()).
                auth().oauth2(config.apiKey()).
                when().
                post("device/{serial}", udid).then().
                statusCode(HTTP_OK);
    }

}
//Take the Device in the before method

package com.mobitru.service;

import org.testng.annotations.*;

public class SmokeNativeTests {

    private final DeviceService deviceService = new DeviceService();

    @Parameters("deviceUdid")
    @BeforeClass
    public void startDriver(String deviceUdid)  {
        deviceService.takeDeviceById(deviceUdid);
    }


}

Release the Device after

The Device should be released after the tests execution to make the device Slot free.
The provided UDID should be used in the appropriate API request.
Below you can find an example for a popular Java rest API library.

package com.mobitru.tests;

import com.mobitru.conf.GlobalConfig;
import io.restassured.RestAssured;
import org.aeonbits.owner.ConfigCache;


import static java.net.HttpURLConnection.HTTP_OK;

public class DeviceService {


    private final GlobalConfig config;

    public DeviceService() {
        this.config = ConfigCache.getOrCreate(GlobalConfig.class);
    }

    public void releaseDeviceById(String udid) {
        RestAssured.
                given().
                baseUri(config.apiUrl()).
                auth().oauth2(config.apiKey()).
                when().
                delete("device/{serial}", udid).then().
                statusCode(HTTP_OK);
    }

}
//Release the Device in the after method

package com.mobitru.service;

import org.testng.annotations.*;

public class SmokeNativeTests {

    private final DeviceService deviceService = new DeviceService();

    @Parameters("deviceUdid")
    @AfterClass
    public void releaseDevice(String deviceUdid)  {
        deviceService.releaseDeviceById(deviceUdid);
    }


}
Scroll to Top