Running one test on several devices with Python

Hi,

I am trying Appium using Python language. And I have a simple script on python for Android (just open app, find text and assert this text.)
I want to run this test on several devices.

**What I already did: **

I have created two android emulators. Run selenium grid hub. Create two configuration files for each emulator and run 2 appium servers:

hub
java -jar selenium-server-standalone-2.53.0.jar -role webdriver -nodeConfig emulator-5554.json -hub http://127.0.0.1:4444/grid/register

Appium servers:

appium --address 127.0.0.1 --port 4721 -bp 2253 --udid emulator-5554 --nodeconfig C:\SeleniumGrid\Android\emulator-5554.json

and

appium --address 127.0.0.1 --port 4722 -bp 2254 --udid emulator-5556 --nodeconfig C:\SeleniumGrid\Android\emulator-5556.json

My json configuration files:

first:

{  "capabilities":
      [
        {
          "deviceName": "emulator-5554",
          "browserName":"Browser", 
          "version":"4.4.4",
          "maxInstances": 1,
          "platform":"ANDROID"
        }
      ],
  "configuration":
  {
    "cleanUpCycle":2000,
    "timeout":30000,
    "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
    "url":"http://127.0.0.1:4721/wd/hub",
    "host": "127.0.0.1",
    "port": 4721,
    "maxSession": 10,
    "register": true,
    "registerCycle": 5000,
    "hubPort": 4444,
    "hubHost": "127.0.0.1"
  }
}

second:

{  "capabilities":
      [
        {
		  "browserName":"Browser", 	
          "deviceName": "emulator-5556",
          "version":"4.4.4",
          "maxInstances": 1,
          "platform":"ANDROID"
        }
      ],
  "configuration":
  {
    "cleanUpCycle":2000,
    "timeout":30000,
    "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
    "url":"http://127.0.0.1:4722/wd/hub",
    "host": "127.0.0.1",
    "port": 4722,
    "maxSession": 10,
    "register": true,
    "registerCycle": 5000,
    "hubPort": 4444,
    "hubHost": "127.0.0.1"
  }
}

And my setUp method:

def setUp(self):
        desired_caps = {}
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = 'platformVersion'
        desired_caps['deviceName'] = 'deviceName'

        desired_caps['app'] = 'oneDo.apk'

        self.driver = webdriver.Remote('http://127.0.0.1:4444/wd/hub', desired_caps)

How can I run this one test on several devices? What should I do next?
May be use any frameworks. Bat what and how? I asked this question everywhere and read everything.

Always what I have known, is to create second script test2.py, but for another device. So each script for each device. But I think itsn’t correct way.

Help me please

You want to put your tests inside a loop so that they run twice. Your second test’s run might run on the same device/emulator as your first test, however, so you need some way to tell the Selenium Hub server to choose your second emulator instead.

The Selenium Hub server chooses which test node to allocate for a test session using an internal class called the DefaultCapabilityMatcher. This class simply checks if a test node matches the desired capabilities of a session request based on the desired capabilities’ platform, browser, and version. For native app Android tests, the browser name field is not used. For all Android tests, the platform must be “Android”. You’re stuck using just the platform version field to have this capability matcher distinguish between the two emulators.

I had a similar problem where I wanted certain tests to run on certain nodes only, so I wrote my own capability matcher. There’s an option when starting Selenium Hub to use a different capability matcher, so that’s how I was able to register my custom matcher with the Hub. You can write your own matcher to check other properties and fields among your test nodes to determine which node a session should run on.

As a final note, I’m not sure how well your tests will run concurrently. I’m not a Python expert, but I believe Python is still restricted by the global interpreter lock problem. You might want to write a Python script that spawns two child processes that each run your test script with different parameters for better concurrency.

Could you please tell me, how can I manipulate nodes. I am new in python and in automation

TL;DR: Ask yourself if you really need to do all the stuff below for your test. Why, exactly, do you need your second test to run on your second emulator?

(All of this would also be much easier if Selenium checked the deviceName capability as part of its matching process by default)

The following is all an example.

First, build your custom capability matcher. As an example, suppose you want to build a capability matcher to distinguish between emulators and real devices, and you want to distinguish between the two using your own capability type called “isEmulator”:

  1. Create a Java project, ideally using Maven or Gradle.
  2. Declare a dependency on org.seleniumhq.selenium:selenium-server:2.52.0
  3. Create a class that implements the CapabilityMatcher interface, that has your own custom node matching logic. The logic is to checks whether the requested capabilities has an “isEmulator” field; if this field exists, return true only for a node that has this provided capability and matches the value in the desired capability.
  4. Build the project. You should get a JAR file as the output.

Now, when you start your Selenium Hub, you want to include your JAR file in the classpath for the Java process, and specify your CapabilityMatcher implementation as the capability matcher for the server to use. As an example, my launch command for Selenium Grid with my capability matcher looks like the following:

java -cp selenium-server-standalone-2.53.0.jar:MY-JAR-FILE \
  org.openqa.grid.selenium.GridLauncher -role hub \
  -capabilityMatcher com.sample.afwang.MyCapabilityLauncher

Next, when I run my tests, I can add in additional capabilities that MyCapabilityLauncher will use to determine whether a node matches or not.

On the test node side, I also specify some provided capabilities the test node provides. My Appium nodeconfig file looks something a bit like:

{
  "capabilities": [
    {
      "version": "4.4.4"
      "maxInstances": "1"
      "platform": "Android",
      "isEmulator": "yes"
    }
  ],
  //blah blah blah, other various configuration items.
}

Now when I start my Appium node, it’ll register itself with the Selenium Hub with the given capabilties, so now the Hub will know that this node provides an “isEmulator” field, and this field’s value’s is “yes”.

In your test script, when you set up your desired capabilities, I would specify something like this:

desired_caps['isEmulator'] = 'yes'

Now when I start a driver, Selenium will use my matcher to check for the isEmulator field, and ensures that the requested capability and the provided capability from my Appium node matches.

I have to try to summarize a lot of content. There’s quite a bit of information condensed here, so take it slow and work diligently on each part until you get to the end. If you don’t know Java, you’ll have to pick up a bit of it to write your capability matcher, and understand how classpaths work.

You should also ask yourself one more question to save yourself all this trouble: Why do you need to have your second test run on the second emulator? If they have the same platform and version, do you really care about testing among the less significant variable differences between your emulators?

Thank you a lot.
But I think it is hard solution. 100% it is should be any framework. For example as in Java I can do it with TestNG.
How people run distributed tests in automation web sites with selenium and python?
I think it should be the same way

Well, I don’t know if there’s a TestNG equivalent for Python, but you can try looking at Python’s threading and multiprocessing libraries for concurrency and parallelism if that’s what you want.

My previous post was about how to specify a particular emulator to run for each test, since test things sequentially usually leads to less thread troubles than trying to get things to run in parallel. My previous post was also made to satisfy your requirement that your second test runs on your second emulator. :smiley:

There is x-dist plugin in pytest,you will need to handle the port configuration for each instance.

I saw it. But it runs test in turns.
Do you have an example of code and example of xdist query?

Here it is
py.test --tx 3*popen --dist=each tests/

It works for two devices. When I have 3 devices
It runs only on two:(
The command is: py.test Test1.py --tx 3*popen --dist=each

That’s great. For the third device, make sure 3 appium server are getting instantiated.
If you could share the console log and the server initialization code, I may be able to check for the cause.

Yes, of course it was running.
Thank you, I will share now