Distributed tests Ruby: Error

I am using cucumber with ruby for mobile tests using appium and I wanted to have distributed tests across multiple simulators in the same mac. I tried implementing the multi thread execution creating two different appium servers for two simulators.

The below code, starts the appium server, However doesnt proceed starting the tests, not sure what am I missing

Any help is great appreciated

require 'appium_lib'
require 'selenium-cucumber'
require 'selenium-webdriver'
require 'rest_client'
require 'json'
require 'mysql2'
require 'thread'


def device1
  {
      caps:

          {

              platformName: "iOS",
              deviceName: "iPhone Simulator", #update device as per your need
              app: (File.join(File.dirname(__FILE__), "something.app")),
              bundleId: "something",
              automationName: "XCUITest",
              xcodeOrgId: "something",
              xcodeSigningId: "iPhone ",
              noReset: "true",
              fullReset: "false",
              showIOSLog: "true",
              autoAcceptAlerts: "true",
              showXcodeLog: "true",
              useNewWDA: "true",
              resetOnSessionStartOnly: "true"
          }
  }
end

def device2
  {
      caps:

          {

              platformName: "iOS",
              deviceName: "iPhone Simulator", #update device as per your need
              app: (File.join(File.dirname(__FILE__), "something.app")),
              bundleId: "something",
              automationName: "XCUITest",
              xcodeOrgId: "something",
              xcodeSigningId: "iPhone ",
              noReset: "true",
              fullReset: "false",
              showIOSLog: "true",
              autoAcceptAlerts: "true",
              showXcodeLog: "true",
              useNewWDA: "true",
              resetOnSessionStartOnly: "true"
          }
  }
end



def start_app(device_id,port_num)
  caps_config= {platformName: "iOS",deviceName: "iPhone Simulator",app: (File.join(File.dirname(__FILE__), "something.app")),bundleId: "something",
                automationName: "XCUITest", xcodeOrgId: "something",xcodeSigningId: "iPhone Developer", udid: device_id}
  appium_lib_config={ port: port_num}
  opts={caps:caps_config,appium_lib:appium_lib_config}
  orig = Appium::Driver.new(opts)
  return orig
end
def test(device1,device2)
  cmd1 = "appium -p 4723 -bp 4002 -U " + device1
  cmd2 = "appium -p 4724 -bp 4003 -U " + device2
  system(cmd1)
  system(cmd2)
  sleep 20
  threads = []
  threads << Thread.new {
    orig = start_app(device1,4000)
    dr = orig.start_driver }
  threads << Thread.new {
    orig = start_app(device2,4001)
    dr = orig.start_driver }


end

I am calling this code using the function test(device1, device2) where device1 and device2 are its udid’s respectively

The output I get

 Welcome to Appium v1.7.0
[Appium] Non-default server args:
[Appium]   bootstrapPort: 4002
[Appium]   udid: (something)
[Appium] Deprecated server args:
[Appium]   -U,--udid => --default-capabilities '{"udid":"something"}'
[Appium] Default capabilities, which will be added to each request unless overridden by desired capabilities:
[Appium]   udid: 'something'
[Appium] Appium REST http interface listener started on 0.0.0.0:4723

@KazuCocoa @wreed Could you please take a look at this with your appium_lib eyes please?

for Deprecated server args:

I recommend you to use --default-capabilities instead of put options directly to Appium servers. And you should read server-args.md more because -bp argument is used for Android and its adb command port. Not iOS.

Do you try to launch multiple simulators against multiple Appium servers?
Launching multiple simulators with Xcode 9 is enough to use single appium server. So, you don’t need to launch multiple appium servers on the machine.
How about the bellow code?

def start_app(device_id, device_name)
  caps_config = {
    platformName: "iOS",
    deviceName: device_name,
    app: (File.join(File.dirname(__FILE__), "something.app")),
    bundleId: "something",
    automationName: "XCUITest",
    xcodeOrgId: "something",
    xcodeSigningId: "iPhone Developer",
    udid: device_id}
  appium_lib_config={ port: 4723 }
  opts={ caps: caps_config, appium_lib: appium_lib_config }
  orig = Appium::Driver.new(opts)
  return orig
end

def test(device1,device2)
  cmd1 = "appium -p 4723"
  system(cmd1)
  sleep 20
  threads = []
  threads << Thread.new {
    orig = start_app(device1, "iPhone 6")
    dr = orig.start_driver
  }
  threads << Thread.new {
    orig = start_app(device2, "iPhone 6s")
    dr = orig.start_driver
  }
  threads.each(&:join)
end

Thanks! This works! I have even taken out, the code to start Appium, since we always have appium servers running in our machines.

Let me work on it and get back to you :slight_smile:

Update: I tried execution basic tests through this code. However, I find that the appium actually crashes the simulator or kills it before proceeding to next

Behaviour

  1. Launch the Application in iOS 11
  2. Kills the Application
  3. Kills the simulator(most times)
  4. Installs webdriver
  5. Exits with xcode 65 error

Also, since we are defining a appium session server start to a variable, is there a way out to promote Appium Methods too? For example, TouchAction does not work with this approach. It says as Undefined method

Update:

I made a few tweeks to the code, so that my existing suite will work. PFB

def start_app(device_id, device_name)
  caps_config = {
      platformName: "iOS",
      deviceName: device_name, #update device as per your need
      app: (File.join(File.dirname(__FILE__), "something.app")),
      bundleId: "something",
      automationName: "XCUITest",
      xcodeOrgId: "something",
      xcodeSigningId: "iPhone",
      Runner: "Distribute",
      #platformVersion: "10.2",
      noReset: "true",
      fullReset: "false",
      showIOSLog: "true",
      autoAcceptAlerts: "true",
      showXcodeLog: "true",
      useNewWDA: "true",
      resetOnSessionStartOnly: "true",
      udid: device_id }
  appium_lib_config={ port: 4723 }
  $opts={ caps: caps_config, appium_lib: appium_lib_config }
  setup

end

def setup()
  Appium::Driver.new($opts)
  Appium.promote_appium_methods Object #Makes all appium_lib methods accessible from steps
  $driver.start_driver  #Starts appium driver before the tests begin

end


def test(device1)
  threads = []
  threads << Thread.new {
    orig = start_app(device1, "iPhone 6")
    orig.start_driver
  }
  threads.each(&:join)

end

This code works with one simulator perfectly and I am able to use all of my existing touch actions.

Update: I created an other thread with a different udid but I see that with the above code, the simulators are launching parallely but the tests are executed sequentially and if the tests fail on one simulator, only then the next simulator starts its webdriver agent and then ends. Also starting the second simulator leads to xcode appium 65 error

For example, TouchAction does not work with this approach.

Yes, XCUITest automationName doesn’t support TouchAction. We should use mobile: action instead.
https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios-xctest-mobile-gestures.md
Regarding the tips, you can read the similar case in no superclass method `text_exact' for RSpec::ExampleGroups · Issue #672 · appium/ruby_lib · GitHub .

This code works with one simulator perfectly and I am able to use all of my existing touch actions.

Yes, the script uses global scope driver $driver, so, the $driver is shared by all instances. You need to use @driver instead of $driver like ruby_lib/ios_tests/parallel/test.rb at master · appium/ruby_lib · GitHub

@KazuCocoa Now my scripts work perfectly after the tweeks, but I had like to know why the tests are running in sequential manner and not parallel?

If you run the following example after launching multiple simulators.

But except for it, launching simulator after the test script start or test scenarios are short, tests run like sequential. How about to run test cases which take 30 sec or a minute to complete them?
@reach2jeyan