Skip to content

Instantly share code, notes, and snippets.

@Tanapruk
Last active March 18, 2017 15:47
Show Gist options
  • Save Tanapruk/7e9af8259fe026d3e71b3cffd777ecd1 to your computer and use it in GitHub Desktop.
Save Tanapruk/7e9af8259fe026d3e71b3cffd777ecd1 to your computer and use it in GitHub Desktop.
Implementing Appium for Android and iOS using java-client through Android Studio

Codes for Android

Android smallest UI unit is View. However, in Appium, the unit is called element instead. We find element instead of View. Before we take any action on the screen, we have to locate the element first.There are various way to findElement from your codes. But some of them don't work well, so I gather here what work as expected for me. Most of them rely on Ui Automation framework. After that, we take action by using built-in methods of element. For example, element.click() to click an element or element.getText() to get word from that element.

You should declare a field variable driver. As follows:

protected static AndroidDriver<AndroidElement> driver;

Find element by Text

Text in this case is a word that is shown on the screen. It will return the first element that has a text Graphics.

driver.findElementByAndroidUIAutomator("text(\"Graphics\")");

Find element by resourceId

resourceId is an attribute of element. You can obtain it by using uiautomatorviewer. It will return the first element that has id of io.appium.android.apis:id/edit.

driver.findElementByAndroidUIAutomator("resourceId(\"io.appium.android.apis:id/edit\")");

Find elements by Text

This is the same as Find Element by Text but this return a list of elements instead of one.

driver.findElementsByAndroidUIAutomator("text(\"Graphics\")");

Find elements by resourceId

This is the same as Find Element by resourceId but this return a list of elements instead of one.

driver.findElementsByAndroidUIAutomator("resourceId(\"io.appium.android.apis:id/edit\")");

Find element through scrolling

Scrolling within android:id/list to the element that has a text of Views.

driver.findElementByAndroidUIAutomator("new UiScrollable(resourceId(\"android:id/list\")).scrollIntoView(text(\"Views\"));")

Get an element from an element list

Get element index of 1 from an element list that has a resourceId of android:id/text.

driver.findElementByAndroidUIAutomator("resourceId(\"android:id/text1\").instance(1)");
apply plugin: 'java'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile group: 'io.appium', name: 'java-client', version: '4.1.2'
testCompile 'org.apache.httpcomponents:httpclient:4.5.2'
testCompile 'cglib:cglib:3.2.4'
testCompile 'commons-validator:commons-validator:1.5.1'
testCompile 'org.apache.commons:commons-lang3:3.5'
testCompile 'commons-io:commons-io:2.5'
testCompile 'org.springframework:spring-context:4.3.5.RELEASE'
testCompile 'org.aspectj:aspectjweaver:1.8.10'
testCompile 'info.cukes:cucumber-java:1.2.4'
testCompile 'info.cukes:cucumber-junit:1.2.4'
testCompile 'junit:junit:4.12'
testCompile 'org.testng:testng:6.9.12'
}
repositories {
jcenter()
maven {
url "http://repo.maven.apache.org/maven2"
}
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

Installation

I want to use Appium as a framework for a UI automation for both iOS and Android. The test code will be written in java. Code editing will be on Android Studio. Mobile Phone devices will be emulator based for both iOS and Android.

1. Appium Server

This server will use our script to automate thing on our device.

  • Install node by brew install node
  • Install appium by npm install -g appium

2. Editor - Android Studio

3. Android Related Tools

Environment Variables

  • You are required to set ANDROID_HOME, JAVA_HOME, and export your PATH within your .bash_profile or .zshrc or any of your shell preference choices. For examples,
  • export ANDROID_HOME=/Users/tanapruk/Library/Android/sdk
  • export JAVA_HOME="/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home"
  • export PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools/bin:$JAVA_HOME/bin

UI Inspection

  • When using your app, in your Terminal use uiautomatorviewer & to lanuch.

Activity Locator

  • You can know your current active Activity by using adb shell dumpsys activity | grep <package> for example, adb shell dumpsys activity | grep com.android.settings and look at mFocusedActivity line.

4. iOS Related Environments

Additional Installation

  • Latest XCode from App Store.
  • brew install ideviceinstaller
  • npm install -g ios-deploy

On iOS Devices

  • Settings > Developer > Enable UI Automation

UI Inspection

5. Best Tutorial/Example

  • Clone project from Java Client.
  • You may have to switch branch to 4.1.2 tag if the latest one doesn't satisfy your need.
  • Use BaseAndroidTest and BaseIOSTest as a super class for your test.
  • Appium Server is automatically started before the test started and ended after the test ended.
  • iOS installation files is packed with .zip(E.g., TestApp.app.zip) while Android installation file is easily an apk file. Put these things anywhere in your project.
  • iOS Capabilities UI Automation for iOS is deprecated. We should use XCUITest instead.
capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "XCUITest");
  • Put your UI Automate Test code in test/java
.
├── build.gradle
├── libs
├── proguard-rules.pro
└── src
    ├── androidTest
    │   └── java
    ├── main
    │   ├── AndroidManifest.xml
    │   ├── java
    │   └── res
    └── test
        └── java

Codes for iOS

<XCUIElementTypeTextField type="XCUIElementTypeTextField" name="IntegerA" label="TextField1" enabled="true" x="119" y="45" width="115" height="37"/>
<XCUIElementTypeTextField type="XCUIElementTypeTextField" name="IntegerB" label="TextField2" enabled="true" x="119" y="90" width="115" height="38"/>
<XCUIElementTypeButton type="XCUIElementTypeButton" name="ComputeSumButton" label="Compute Sum" enabled="true" x="110" y="143" width="133" height="45"/>

Above is a layout that we see from UI Inspection Viewer.

  • name is like an id.
  • label is text on the screen.

You should declare a field variable driver. As follows:

protected static IOSDriver<MobileElement> driver;

Find element by name

driver.findElementByName("ComputeSumButton")

Find element by either name or label

driver.findElementByAccessibilityId("ComputeSumButton")
driver.findElementByAccessibilityId("Compute Sum")
//both returns the same element

Find element by xpath

driver.findElementByXPath("//XCUIElementTypeButton[@name='ComputeSumButton']")

Appium Server

When you call appium inside Terminal, you are initializing a web server.

[Appium] Welcome to Appium v1.6.3
[Appium] Appium REST http interface listener started on 0.0.0.0:4723

Get Status

If you want to talk with this server, you have to know its routes. For example, to get status you have to put path /wd/hub/status/ behind the url, you can try typing 0.0.0.0:4723/wd/hub/status in your web browser. The server will return a json text of {"status":0,"value":{"build":{"version":"1.6.3","revision":null}},"sessionId":null}

Get Session

If you have POSTMAN you can submit POST by attaching body of

{"desiredCapabilities":{"app":"ApiDemos-debug.apk","platformName":"Android","deviceName":"emulator-5554","avd":"TestDevice"}}

to 0.0.0.0:4723/wd/hub/session

and see how it responses back. This is how I was responsed.

{
  "status": 0,
  "value": {
    "platform": "LINUX",
    "webStorageEnabled": false,
    "takesScreenshot": true,
    "javascriptEnabled": true,
    "databaseEnabled": false,
    "networkConnectionEnabled": true,
    "locationContextEnabled": false,
    "warnings": {},
    "desired": {
      "app": "ApiDemos-debug.apk",
      "platformName": "Android",
      "deviceName": "emulator-5554",
      "avd": "TestDevice"
    },
    "app": "ApiDemos-debug.apk",
    "platformName": "Android",
    "deviceName": "emulator-5554",
    "avd": "NextzyTestDevice",
    "deviceUDID": "emulator-5554",
    "platformVersion": "7.1.1",
    "deviceScreenSize": "1080x1920",
    "deviceModel": "Android SDK built for x86",
    "deviceManufacturer": "unknown",
    "appPackage": "io.appium.android.apis",
    "appWaitPackage": "io.appium.android.apis",
    "appActivity": "io.appium.android.apis.ApiDemos",
    "appWaitActivity": "io.appium.android.apis.ApiDemos"
  },
  "sessionId": "1b96eebe-5656-4b80-a45f-ef5d18b8aa4a"
}

Find Element

If you successfully launch your Android phone/emulator, instead of waiting to run findElement in your codes, you can make a request like belows: Get your sessionId from previous response and put it to the path below, shot POST to 0.0.0.0:4723/wd/hub/session/1b96eebe-5656-4b80-a45f-ef5d18b8aa4a/element with a json of

{"using":"-android uiautomator","value":"text(\"Accessibility\")"}

Your response will be:

{
  "status": 0,
  "value": {
    "ELEMENT": "1"
  },
  "sessionId": "1b96eebe-5656-4b80-a45f-ef5d18b8aa4a"
}

Click Element

Add 1 to path, /click path and attach a json of {"id":"1"} to 0.0.0.0:4723/wd/hub/session/1b96eebe-5656-4b80-a45f-ef5d18b8aa4a/element/1/click. Submitted by POST method.

Here is the response.

{
  "status": 0,
  "value": true,
  "sessionId": "1b96eebe-5656-4b80-a45f-ef5d18b8aa4a"
}

Programming flows

Your java-client, essentially convert your test codes into query string and submit to the appium server. When the client receive responses, it convert json strings into meaningful codes/actions/execution.

@Tanapruk
Copy link
Author

Tanapruk commented Mar 17, 2017

AppiumFlow for Android.
appiumflow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment