Start Appium Programmatically

Hey ,

I Have a Testng file with 2 classes and several methods

I have a method that start appium programmically on a random port.

@BeforeClass
public void startAppium(){
AppiumDriverLocalService service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder().usingAnyFreePort());
service.start();
appiumServiceUrl = service.getUrl().toString();
}

Before Each method I instantiate the driver.

@BeforeMethod
public void prepareDevice(){

driver = new AndroidDriver(new URL(appiumServiceUrl), capabilities);

}

But because My variable ‘appiumServiceUrl’ gets null after a class, i have to execute it again before each class.
Anyway I can prevent this ?

Create a BaseTestPage which does this for you. Extend the page in your testPage i.e

sample :

BaseTest

public abstract BaseTest {

private static AndroidDriver androidDriver;

@BeforeSuite
public abstract void setUpBeforeSuite()

@BeforeClass
public abstract void setUpBeforeClass()

@BeforeTest
public abstract void setUpBeforeTest()

etc etc

MyTest

public class MyTest extends BaseTest {

implement extended classes methods to your need

}

Hi,

I do have a general Setup Class which includes the methods mentioned in my first post.

package Setup;

public class Setup {

public AppiumDriver driver;
public String appiumServiceUrl;
@BeforeClass
public void startAppium(){
    AppiumDriverLocalService service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder().usingAnyFreePort());
    service.start();
    appiumServiceUrl = service.getUrl().toString();
}
@Parameters({ "device_id","device_name","platformVersion", "platformName"})
@BeforeMethod
public void prepareDeviceForAppium(String device_id, String device_name, String platformVersion, String platformName) throws Exception {
        DesiredCapabilities capabilities = new DesiredCapabilities();
        ...
        
        driver = new AndroidDriver(new URL(appiumServiceUrl), capabilities);
           }
@AfterMethod
public void tearDown() throws Exception {
    driver.quit();
}

}

Problem is that when i Try @BeforeTest before startAppium(), i can’t initiate the driver for the next method, since “appiumServiceUrl” is null.

My testCase do extends this setup class

public class Activation extends Setup{

Normally this would be the order of running if we extend it from another class :

parentTest - BeforeTest- parent testClass1 - BeforeTest- test1 parentTest - BeforeClass- parent testClass1 - BeforeClass- test1 parentTest - BeforeMethod- parent testClass1 - BeforeMethod- test1 testClass1 - myTestMethod1 testClass1 - AfterMethod- test1 parentTest - AfterMethod- parent parentTest - BeforeMethod- parent testClass1 - BeforeMethod- test1 testClass1 - myTestMethod2 testClass1 - AfterMethod- test1 parentTest - AfterMethod- parent testClass1 - AfterClass- test1 parentTest - AfterClass- parent testClass1 - AfterTest- test1 parentTest – AfterTest- parent

Considering the hierarchal execution, you should reconsider in using @BeforeTest vs startAppium() of @BeforeClass as @BeforeTest executes first before @BeforeClass.

Hope this helps in understanding.

Thx for the overview, But the issue is not in the use of the annotations I think.

The first thing I do in my code is starting the appium server. Then before every method the androiddriver is initialized on that server using the variable ‘appiumserviceurl’.

Using @BeforeTest, there is no issue running the cases on multiple devices as long as the ‘@Test’ methods are within the same class.

The moment I add a method from another class in my TestNG file, it fails. The reason of failing is that the variable ‘appiumserviceurl’ is set to null, so the Androiddriver can’t be initialized for the method of the new class.

Using @Beforeclass for my startAppium() works fine, but then for each class a new appiumserver is started. This is not wanted. All method between tags should be executed on the same appium server.

So I have the find a solution to keep the value of AppiumServiceUrl. I Can’t make it static since I need to run my script parrallel on multiple devices.

Could you explain more on ' all methods between tags should be executed on the same appium server' ???

I understand that you want to run appium server on different ports for different devices right and passing the same parameters in testng-xml right ?

indeen one Test Tag = one device. And per device a appium server on a different port is launched.

<?xml version="1.0" encoding="UTF-8"?>
    <parameter name="device_id" value="c5b56570"/>
    <parameter name="device_name" value="samsung"/>
    <parameter name="platformVersion" value="6.0.1"/>
    <parameter name="platformName" value="Android"/>
    <classes>
        <class name="Scenarios.Activation">
            <methods>
                <include name="Carrousel"/>
                <include name="Activation"/>
            </methods>
        </class>
        <class name="Scenarios.Add">
            <methods>
                <include name="CompleteAdd"/>
            </methods>
        </class>
    </classes>
</test>
<test name="Device2">
    <parameter name="device_id" value="ZX1G423C29"/>
    <parameter name="device_name" value="samsung"/>
    <parameter name="platformVersion" value="7.0"/>
    <parameter name="platformName" value="Android"/>
    <classes>
        <class name="Scenarios.Activation">
            <methods>
                <include name="Carrousel"/>
                <include name="Activation"/>
            </methods>
        </class>
        <class name="Scenarios.Add">
            <methods>
                <include name="CompleteAdd"/>
            </methods>
        </class>
    </classes>
</test>

Does it work for you when you have only "@BeforeMethod" which includes the

AppiumDriverLocalService service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder().usingAnyFreePort()); service.start(); appiumServiceUrl = service.getUrl().toString();

following code in addition to Capability and Driver code.

Kindly let me know if that works for you.

But then a new appium server will be started for each new method ?

Well I myself hasn’t personally tested it yet but you can give a try and let me know the result. Did you also try with '@BeforeSuite' too ?

@BeforeMethod will work perfectly, that I Am sure. But i don’t want a new appium server to be started before every method.

@BeforeSuite gives the same problem as @BeforeTest.

Did you include the keyword parallel="methods" in your xml suite ? If not - you should probably use the same or use selenium-grid to configure your driver and capabilities relevantly.

or even try this link “https://github.com/SrinivasanTarget/AppiumIOSParallel” - Hopefully it may be of some use to you.

Hi,

I think You don’t understand my issue. I don’t have issues with parallel testing. All works perfect.

The only issue I have is that with my current setup I always have to start a new appium server when I start running methods from another class.

To conclude, my current setup works perfectly, the only amelioration I want to make is that only one appium server is started per tested device.

I recommend you use this:
AppiumDriverLocalService appiumService = AppiumDriverLocalService.buildDefaultService();
and also you have to add ANDROID_HOME variable enviroment in TESTNG runner. try that… if you have any questions you can ask me :slight_smile:

Can you explain why:

I should replace my

by

AppiumDriverLocalService appiumService = AppiumDriverLocalService.buildDefaultService();

I Can’t specify a port, so impossible to execute parallel testing ?

What is the purpose of this ? Android_Home Variable is already added to my $PATH variable

Thanks in advance

@ManDD : Hey ! I understood your point very well.

As per my understanding,

AppiumDriverLocalService service = AppiumDriverLocalService.buildService(new AppiumServiceBuilder().usingAnyFreePort()); leverages and ensures the randomization of port and service is started relevantly.

I would suggest if you can use – Process builder for building the cmd-line for starting appium for different devices

for example : node . -p 4492 -bp 2251 -U 32456 && node . -p 4491 -bp 2252 -U 43364 – reference @ http://appium.io/slate/en/master/?java#parallel-android-tests

Ah, I certainly forgot that you were doing tests in parallel. About ANDROID_HOME variable, when you run appium (in Eclipse IDE) as you want to do it will not detect the environment variable that you have modified in the $PATH variable. That’s why you should do this:

I recommend using selenium-grid for parallel testing along with testng.

U can watch this video : https://youtu.be/uA8x3geLanA

Hi ferruzoft,

Could you help to solve the similar issue using C#?

I have set all path in bash_profile.

export ANDROID_HOME=/Users/xxx/Library/Android/sdk
export JAVA_HOME=$(/usr/libexec/java_home)
export PATH=${JAVA_HOME}/bin:$PATH
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platforms-tools
export PATH=${PATH}:${ANDROID_HOME}/build-tools/27.0.1
export PATH=${PATH}:${JAVA_HOME}

When I open Appium desktop and start the server at first, everything is fine and enable to detect adb device. Also, using the command line like (appium -p 4723) no issues.

However, if I directly run in visual studio without opening appium server at first,

        AppiumServiceBuilder appiumServiceBuilder = new AppiumServiceBuilder()
            .UsingAnyFreePort();

        AppiumLocalService appiumLocalService = appiumServiceBuilder.Build();
        if (!appiumLocalService.IsRunning)
            appiumLocalService.Start();
        DesiredCapabilities capabilities = new DesiredCapabilities();

        capabilities.SetCapability("deviceName", "EM1");
        capabilities.SetCapability("platformVersion", "6.0");
        capabilities.SetCapability("platformName", "Android");
        capabilities.SetCapability("appPackage", "com.thlonline.telme");
        capabilities.SetCapability("appActivity", "com.thlonline.telme.MainActivity");
    
        AndroidDriver<AndroidElement> androidDriver = new AndroidDriver<AndroidElement (appiumLocalService, capabilities);

the error occurs as follows:

[ADB] The ANDROID_HOME environment variable is not set to the Android SDK root directory path. ANDROID_HOME is required for compatibility with SDK 23+. Checking along PATH for adb.

The full log as follows:

[Appium] Welcome to Appium v1.7.2
[Appium] Non-default server args:
[Appium] address: 127.0.0.1
[Appium] Appium REST http interface listener started on 127.0.0.1:4723
[HTTP] → GET /wd/hub/status {}
[debug] [MJSONWP] Calling AppiumDriver.getStatus() with args:
[debug] [MJSONWP] Responding to client with driver.getStatus() result: {“build”:{“version”:“1.7.2”,“revision”:null}}
[HTTP] ← GET /wd/hub/status 200 15 ms - 72
[HTTP] → POST /wd/hub/session {“desiredCapabilities”:{“deviceName”:“EM1”,“platformVersion”:“6.0”,“platformName”:“Android”,“appPackage”:“com.thlonline.telme”,“appActivity”:“com.thlonline.telme.MainActivity”}}
[debug] [MJSONWP] Calling AppiumDriver.createSession() with args: [{“deviceName”:“EM1”,“platformVersion”:“6.0”,“platformName”:“Android”,“appPackage”:“com.thlonline.telme”,“appActivity”:“com.thlonline.telme.MainActivity”},null,null]
[debug] [BaseDriver] Event ‘newSessionRequested’ logged at 1520906109271 (14:55:09 GMT+1300 (NZDT))
[Appium] Creating new AndroidDriver (v1.37.0) session
[Appium] Capabilities:
[Appium] deviceName: EM1
[Appium] platformVersion: 6.0
[Appium] platformName: Android
[Appium] appPackage: com.thlonline.telme
[Appium] appActivity: com.thlonline.telme.MainActivity
[BaseDriver] Session created with session id: 930fe437-057c-4667-9f42-f09d025b201f
[debug] [AndroidDriver] Getting Java version
[AndroidDriver] Java version is: 1.8.0_131
[ADB] Checking whether adb is present
[ADB] The ANDROID_HOME environment variable is not set to the Android SDK root directory path. ANDROID_HOME is required for compatibility with SDK 23+. Checking along PATH for adb.
[ADB] Error: Could not find adb Please set the ANDROID_HOME environment variable with the Android SDK root directory path.
at Object.wrappedLogger.errorAndThrow (/usr/local/lib/node_modules/appium/node_modules/appium-support/lib/logging.js:69:13)
at ADB.callee$0$0$ (/usr/local/lib/node_modules/appium/node_modules/appium-adb/lib/tools/system-calls.js:127:9)
at tryCatch (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:67:40)
at GeneratorFunctionPrototype.invoke [as _invoke] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:315:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as throw] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:100:21)
at GeneratorFunctionPrototype.invoke (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:136:37)
at
Error: Could not find adb Please set the ANDROID_HOME environment variable with the Android SDK root directory path.
at Object.wrappedLogger.errorAndThrow (/usr/local/lib/node_modules/appium/node_modules/appium-support/lib/logging.js:69:13)
at ADB.callee$0$0$ (/usr/local/lib/node_modules/appium/node_modules/appium-adb/lib/tools/system-calls.js:127:9)
at tryCatch (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:67:40)
at GeneratorFunctionPrototype.invoke [as _invoke] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:315:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as throw] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:100:21)
at GeneratorFunctionPrototype.invoke (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:136:37)
at
[debug] [AndroidDriver] Shutting down Android driver
[debug] [AndroidDriver] Called deleteSession but bootstrap wasn’t active
[MJSONWP] Encountered internal error running command: Error: Could not find adb Please set the ANDROID_HOME environment variable with the Android SDK root directory path.
at Object.wrappedLogger.errorAndThrow (/usr/local/lib/node_modules/appium/node_modules/appium-support/lib/logging.js:69:13)
at ADB.callee$0$0$ (/usr/local/lib/node_modules/appium/node_modules/appium-adb/lib/tools/system-calls.js:127:9)
at tryCatch (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:67:40)
at GeneratorFunctionPrototype.invoke [as _invoke] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:315:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as throw] (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:100:21)
at GeneratorFunctionPrototype.invoke (/usr/local/lib/node_modules/appium/node_modules/babel-runtime/regenerator/runtime.js:136:37)
at
[HTTP] ← POST /wd/hub/session 500 265 ms - 246

I investigated the whole day, looks like in Java needs to set ANDROID_HOME in the IDE, but I don’t know how to do this in visual studio using C#. Please help, many thanks!

1 Like

Oh wow, I’ve never tried it with C# but I can try. Meanwhile, I suggest you to review the next youtube channel: https://www.youtube.com/watch?v=tr_4HLYCyfs&list=PL6tu16kXT9PqEHCwx3QXaNpFn9wt8I8-4
maybe this helps you. :slight_smile: