Serenity-core: Managing multiple appium device capabilities

Created on 19 Feb 2020  路  4Comments  路  Source: serenity-bdd/serenity-core

Hello,

I am writing tests for a mobile browser chat application using Screenplay and Appium
My tests can have multiple Actors which may use different devices

My question is: What is the recommended way to manage multiple devices in a single test?

I've tried using serenity.properties to configure properties such as:

appium.hub = http://127.0.0.1:4723/wd/hub
appium.platformName = Android
appium.platformVersion = 5.1.1
appium.deviceName = e2f5c460
appium.browserName = Chrome

However, this seems to only support a single device.
It's unclear if something similar is supported in serenity.conf

Currently, I'm considering something like this to configure my capabilities

public DesiredCapabilities capabilities(String deviceModel, String browserName) {

        DesiredCapabilities capabilities;

        if (deviceModel.equals("Samsung S10")) {
                        capabilities = DesiredCapabilities.android();
                capabilities.setCapability("platformName", "Android");
            capabilities.setCapability("deviceName", "879e6464");
            capabilities.setCapability("platformVersion","10.0");
        }

        if (deviceModel.equals("iPhone XS")) {
                        capabilities = DesiredCapabilities.iphone();
                        capabilities.setCapability("platformName", "iOS");
            capabilities.setCapability("platformVersion","12");
            capabilities.setCapability("deviceName", "iPhone XS");
        }

               if( capabilities != null) {
                   capa.setCapability("browserName", browserName);
               }

        return capabilities;
    }

Any advice is appreciated :)

question

Most helpful comment

You can use multiple devices, because u can specify to each actor which device to use, I have done it using something like:

class MobileActorsCast() : Cast() {
    override fun actorNamed(actorName: String, vararg abilities: Ability?): Actor {
        val deviceId = getCurrentAndroidDevices().last()
        return super.actorNamed(actorName, UseAMobileDevice.with(theDefaultDriverFor(actorName, deviceId)))
    }

    private fun theDefaultDriverFor(actorName: String, deviceId: String): WebDriver {
        return ThucydidesWebDriverSupport.getWebdriverManager()
                .withOptions("deviceName=$deviceId;udid=$deviceId")
                .getWebdriverByName(actorName)
    }

    private fun getCurrentAndroidDevices(): List<String> {
        val process = ProcessBuilder("adb", "devices")
                .redirectErrorStream(true)
                .start()
        val output = readOutput(process.inputStream)
        process.waitFor()
        return output
                .mapNotNull {
                    it.substringBefore("\tdevice", "")
                            .ifEmpty { null }
                }
                .ifEmpty { error("There are no Android devices connected") }
    }

    private fun readOutput(inputStream: InputStream): List<String> {
        return BufferedReader(InputStreamReader(inputStream)).use {
            it.lines().toList()
        }
    }
}

UseAMobile is just an ability that I made.

class UseAMobileDevice(private val webDriver: WebDriver) : BrowseTheWeb(webDriver) {
    override fun toString(): String {
        return "Use a Mobile device"
    }

    fun getAndroidDriver(): AndroidDriver<*> {
        return ((webDriver as WebDriverFacade).proxiedDriver as AndroidDriver<*>)
    }

    companion object {
        fun with(webDriver: WebDriver) = UseAMobileDevice(webDriver)
    }
}

All 4 comments

There isn't currently any built-in support for multiple devices in a built-in test.

Thank you for your reply.
I did some digging through the code. You are correct, there appears to be multiple difficulties with achieving this. I did come across AppiumConfiguration.from(environmentVariables).getCapabilities(options) which can at least generate my desired capabilities. or I can specify the options in the @Managed tag

That being said, I would love to see something like:

devices{
    iPhoneXS {
        platformName = IOS
        platformVersion = 12
        deviceName = "iPhone XS"
    }
    SamsungS10 {
        platformName = "Android"
        platformVersion = 10.0
        deviceName = 879e6464
    }
}

AppiumConfiguration.from(environmentVariables).getCapabilitiesForDevice("iPhoneXS")

You can use multiple devices, because u can specify to each actor which device to use, I have done it using something like:

class MobileActorsCast() : Cast() {
    override fun actorNamed(actorName: String, vararg abilities: Ability?): Actor {
        val deviceId = getCurrentAndroidDevices().last()
        return super.actorNamed(actorName, UseAMobileDevice.with(theDefaultDriverFor(actorName, deviceId)))
    }

    private fun theDefaultDriverFor(actorName: String, deviceId: String): WebDriver {
        return ThucydidesWebDriverSupport.getWebdriverManager()
                .withOptions("deviceName=$deviceId;udid=$deviceId")
                .getWebdriverByName(actorName)
    }

    private fun getCurrentAndroidDevices(): List<String> {
        val process = ProcessBuilder("adb", "devices")
                .redirectErrorStream(true)
                .start()
        val output = readOutput(process.inputStream)
        process.waitFor()
        return output
                .mapNotNull {
                    it.substringBefore("\tdevice", "")
                            .ifEmpty { null }
                }
                .ifEmpty { error("There are no Android devices connected") }
    }

    private fun readOutput(inputStream: InputStream): List<String> {
        return BufferedReader(InputStreamReader(inputStream)).use {
            it.lines().toList()
        }
    }
}

UseAMobile is just an ability that I made.

class UseAMobileDevice(private val webDriver: WebDriver) : BrowseTheWeb(webDriver) {
    override fun toString(): String {
        return "Use a Mobile device"
    }

    fun getAndroidDriver(): AndroidDriver<*> {
        return ((webDriver as WebDriverFacade).proxiedDriver as AndroidDriver<*>)
    }

    companion object {
        fun with(webDriver: WebDriver) = UseAMobileDevice(webDriver)
    }
}

I've come up with my own solution for this. See this sample project.
I would gladly submit a PR if it's useful for others. I would need to solve #2289 before it would be useful to JUnit users though.

Was this page helpful?
0 / 5 - 0 ratings