Mobile Applications have become an essential part of any successful business. Hence, Mobile Application Development is growing across the tech industry and the trend will continue to grow in the coming years.
After a mobile application has been developed, one of the main goals is to provide the best experience possible to the end users. A thorough Mobile Application Testing is thus necessary to successfully release the app to the production environment. This can be done through testing manually or by using a test automation tool like Appium. A good test automation tool should be able to test for its functionality, usability and consistency.
Challenges of testing a Mobile App vary from limitations in device processing speed to multiple browsers/ platforms, multiple network types, limited memory size and different device communication protocols.
Mobile Applications can be of 3 types (Native Apps, Mobile Web Apps and Hybrid Apps). “Native” apps are the ones created using the SDKs (Software Development Kits) of Android, iOS or Windows. The “Mobile Web” apps are the ones which can be accessed using the mobile browsers like Chrome, Safari etc. “Hybrid” apps are the ones which have a wrapper around the “webview” native control that enables interaction with web content. All of these types can be tested through automation using Appium (along with your preferred language) on iOS, Android or Windows desktop platform.
The name “Appium” was derived from “Selenium for Apps” and is an Open Source Project donated to the JS Foundation in 2016. It was developed on the idea that including the SDKs or recompilation of the app should not be required while performing automation on the apps.
Let’s go through the architecture and see how Appium is designed:
INTRODUCTION AND ARCHITECTURE
Appium is an Open Source Test Automation Framework which can be used to test Mobile Native App, Mobile Web App and Hybrid App (Native+Web). Appium is also “cross-platform” which means that Appium tests can be written to run on multiple mobile platforms.
Platform Support
Appium API allows us to write tests against iOS, Android and Windows platforms. It uses vendor-provided automation frameworks for compilation tasks. The supported frameworks are:
– For Android 2.3+ Google’s Instrumentation
– For Android 4.2+ Google’s UiAutomator/UiAutomator2
– For iOS 9.3 and lower Apple’s UIAutomation
– For iOS 9.3 and higher Apple’s XCUITest
– For Windows Microsoft’s WinAppDriver
The “Appium Drivers” for these vendor-specific technologies are at the heart of supporting tests using Appium in the different platforms.
Client Libraries
Appium is based on the Client-Server Architecture and the Appium server needs “Client Libraries” to get instructed on what kind of session to start and what kind of automation to perform. Since, Appium speaks the W3C WebDriver protocol, we can use the standard Selenium client libraries. But Appium can perform additional actions on Mobile Devices which Selenium can’t. Hence, the Selenium client libaries had been wrapped with more functionality (e.g. multitouch gestures, screen orientation) and Appium Client Libraries were formed which is preferred to run our Appium tests. They implement the Mobile JSON Wire Protocol (Mobile JSONWP, which is mobile-specific extension to the JSON Wire Protocol (JSONWP)) and elements of W3C WebDriver specification since the client-server implementation requires a communication spec in general. The purpose of the clients is to provide an interface in a given language, hiding the protocol details. The Appium server does not care what client we are using, it only cares about the protocol. The client libraries are made up of the following languages/frameworks:
- JAVA
- PYTHON
- RUBY
- JAVASCRIPT
- OBJECTIVE C
- C#
- PHP
- ROBOT FRAMEWORK
Architecture
Appium is based on Client/ Server Architecture. It is simply an HTTP Web Server (written in Node.js) that exposes a REST API. It receives connection requests from client, listens and executes the commands on a mobile device and sends back the response. Due to this architecture, we can write tests on any language having HTTP Client API, but preferably Appium’s own Client Libraries are used. These libraries support Appium’s extensions to the WebDriver protocol.
Once Appium is downloaded and installed on your machine, the HTTP Web Server will be set up exposing the REST API. Once the Appium scripts are executed (from command line, from your favorite IDE or from Appium Desktop), the Client Library will initiate an iOS or Android session with the Appium server and will make a POST (/session) request with the Desired Capabilities JSON object through Mobile JSON Wire Protocol. The server will then send back SessionID to the client to send further commands to the server.
For each session, once the Appium server receives the execution commands, it will send those commands to the vendor-specific testing frameworks (like UiAutomator2, XCUITest) which will then perform the command execution on the mobile devices platform and send back HTTP Responses to the Appium server. The Appium server will then send back responses to the client stating whether the script has been successfully executed or not.
INSTALLATION
Setting up Appium in your machine might look a little intimidating because of the number of steps involved. We will go through each step one by one in Windows machine and ensure that you are able to run your first Appium script without any roadblock.
1) Install Java and set Java Environment Variable Path
Java Development Kit (JDK) is an application to create and modify programs in Java. To install Java in your system, download JDK from Oracle JDK Downloads page. After download, go through the installation process and complete it. Once installation is complete, go to your Environment Variables and set up JAVA_HOME as system/user variable and include %JAVA_HOME%\bin in your path variable. To check whether Java has been successfully installed or not, open your command prompt, type java -version and press Enter. It should display the JDK version that your system is currently referring to.
2) Install Android Studio and set Environment Variable Path
Android is an Operating System developed by Google for mobile devices. It is a platform that provides tools which we need to run our Appium Tests. You can download Android Studio from Google Android Developer website.
Once Android Studio is successfully downloaded, start installation. It will take some time to complete the installation process. After installation is complete, you should be able to see Android folder inside the Local folder of AppData (e.g C:/Users/AppData/Local/Android). By default, the AppData folder is hidden. Ensure that it is unhidden. To set the Android Environment Variable path:
- Go To Control Panel -> System and Security -> System
- Click on “Advanced system settings”
- Go to “Advanced” tab and click on “Environment Variables”
- In System Variables section, set ANDROID_HOME variable with the path of your Android SDK folder (image below)
- Edit the already existing “Path” variable and add the location of tools, tools\bin and platform-tools folder of Android SDK (image below)
- Click on OK and you are all set.
3) Open Android Studio and Configure Emulator (Virtual Device)
If you are not using Real Device to run your Appium Tests, you need to configure Emulator or Virtual Device from your Android Studio that you have just installed. Open Android Studio by clicking on the icon. Once opened, click on the AVD Manager icon at the top right corner of Android Studio.
“Android Virtual Device Manager” window will open up. Click on “+Create Virtual Device” button below and perform the below steps to optimize the Virtual Device performance:
- Select any device (preferably Nexus5 or Nexus6)
- Click on “Clone Device“
- In Configure Hardware Profile window, give your Device a name
- Select Device Type as Phone/Tablet
- Edit Memory field to take 850 MB RAM (particularly if your System installed memory is 4GB/8GB)
- Uncheck all the checkboxes in Cameras and Sensors sections.
- Click on “Finish” button
- In Select Hardware window, click on Next button
- In System Image window, click on image (e.g. Pie/Oreo) to download. It will take some time to download.
- Verify the Configuration in AVD window and click on Finish. You will see an entry like below. Click on the Green “Play” button under the Actions column to start your virtual device.
Note:- To check whether your virtual device is running or not, go to command prompt, type the command adb devices and press Enter key. You should be able to see your virtual device.
4) Download and install Node.js
We need Node.js to run our Appium server. Go to Nodejs official download page, click on the LTS tab and download Windows Installer package (.msi) or Windows Binary (.zip). Once downloaded, install NodeJS. Like before, go to Environment Variables and set NODE_HOME variable with the location of nodejs folder in your Program Files (e.g. C:\Program Files\nodejs). Edit the Path variable and add %NODE_HOME%\;%NODE_HOME%\node_modules\npm\bin to its existing value. To check whether nodejs is successfully installed on your system, go to command prompt, type node -v and press Enter key. You should be able to see the version of nodejs you have installed.
5) Download Appium Server using npm (Node Package Manager)
If you have followed the previous step to install nodejs, then the Node Package Manager (npm) is already set in your system. To check whether npm is set or not, open command prompt, type npm -v and press Enter key. It will display the installed npm version. Now, to download Appium Server using npm, type the command npm install -g appium in command prompt and press Enter. It should start downloading all the packages and dependencies required for Appium Server to run. Once completed, you can check the Appium version installed by running the command appium -v. To start the Appium server, you need to use the command appium and to stop the server anytime you can use Ctrl+C command.
6) Download Appium Java Client Library
In this tutorial we will be running our Appium tests using Java and hence we require to download the Appium Java Client Library from the Appium official page. An executable jar file (e.g. java-client-7.0.0) will get downloaded. You also need to download Selenium Java Client Libraries (Download from Selenium official page) and Apache commons-lang3(Download from Apache Commons official page).
FIRST APPIUM TEST
To run your first Appium test, open Eclipse (or any other Java IDE of your choice) and create a new Java Project by following the steps in the picture below. Here, the project name is given as SampleAppiumProject.
Once your project is created successfully, create a package (Right-click on project -> New -> Package). Here, the package name is given as core. You can give any other meaningful name. Now create a class (here, class name is given as AppiumBase) with a suitable class name of your choice starting with uppercase. To create a class, right click on package -> New -> Class -> Give class name -> Finish. Next, we have to set the dependencies in the Project Build Path. Right click on the project -> Build Path -> Configure Build Path -> Libraries tab -> Click on “Add External JARs…” -> Import the Appium java client library jar, the Selenium Java Client library jars and Apache commons-lang3 jar file that you downloaded earlier. Your SampleAppiumProject structure now should look like below:
apk Files
apk (Android Package Kit), sometimes also referred to as Android Application Package, is a file format which is used by the Android Operating System for installation and distribution of mobile apps. Just like exe file on Windows, you can place a .apk file on your Android device to install an app. When you download an app from Google play store, it automatically downloads and installs the apk file for you. Here, in this tutorial we will be using the ApiDemos-debug.apk file. You can download/clone it from the Appium Java-Client Github page. Create a folder (you can name it app or any name of your choice) in your SampleAppiumProject by doing Right-click -> New -> Folder -> Give Folder name -> Finish. Place the ApiDemos-debug.apk file that you cloned under this folder.
DesiredCapabilities
Let’s create a static method (initializeAppiumCapabilities) inside the AppiumBase class whose purpose will be to initialize and set the Appium capabilities to run our Appium tests. The method returns AndroidDriver<AndroidElement>. Inside the method, write the following code:
public static AndroidDriver<AndroidElement> initializeAppiumCapabilities() {
AndroidDriver<AndroidElement> androidDriver = null;
try {
// Set the desired capabilities
File androidApkfile = new File("./app/ApiDemos-debug.apk");
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Nexus_5X_Edited_API_28");
desiredCapabilities.setCapability(MobileCapabilityType.APP, androidApkfile.getAbsolutePath());
desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "uiautomator2");
First we are initializing the AndroidDriver<AndroidElement> type variable androidDriver to null. Then we are creating a File object and passing the location of the apk file in its constructor. DesiredCapabiltiies are key-value pairs which are sent to the Appium server to inform it about the session, mobile os platform to be used (iOS/ Android/ Windows), device name (the kind of mobile driver or emulator to be used, here emulator name is “Nexus_5X_Edited_API_28“), automation name (which automation engine to use e.g. uiautomator2 for Android, XCUITest for iOS) and the absolute local path or remote http URL to a .apk file (Android), .apks file (Android App Bundle), .ipa file (iOS), .app folder (iOS Simulator) or a .zip file containing one of these. Appium supports many capabilities. We will be exploring some more in our upcoming tutorials.
// Declare the Appium Server url and create AndroidElement object
URL appiumServerUrl = new URL("http://127.0.0.1:4723/wd/hub");
androidDriver = new AndroidDriver<AndroidElement>(appiumServerUrl, desiredCapabilities);
androidDriver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
return androidDriver;
}
Next we create an URL object and define the http url in which our Appium server will be running. Typically the server will be running at port 4723 of localhost. Now, create an object of the AndroidDriver class and pass the appiumServerUrl object and desiredCapabilities object as its Constructor parameters. You can define webdriver implicit wait too. We have set it to 15 seconds. The AppiumBase class will now look like:
package core;
import java.io.File;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.remote.DesiredCapabilities;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.remote.MobileCapabilityType;
public class AppiumBase {
public static AndroidDriver<AndroidElement> initializeAppiumCapabilities() {
AndroidDriver<AndroidElement> androidDriver = null;
try {
// Set the desired capabilities
File androidApkfile = new File("./app/ApiDemos-debug.apk");
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Nexus_5X_Edited_API_28");
desiredCapabilities.setCapability(MobileCapabilityType.APP, androidApkfile.getAbsolutePath());
desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "uiautomator2");
// Declare the Appium Server url and create AndroidElement object
URL appiumServerUrl = new URL("http://127.0.0.1:4723/wd/hub");
androidDriver = new AndroidDriver<AndroidElement>(appiumServerUrl, desiredCapabilities);
androidDriver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
} catch (Exception e) {
e.printStackTrace();
}
return androidDriver;
}
}
Let us now create a test class which will contain our first Appium Test. Like before, create a new class inside a new package (test) and name it “AppiumBasicTest”. Ensure that the class is having the main method inside it and the class is extending the “AppiumBase” class (Inheritance). Now write the below code:
package test;
import core.AppiumBase;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
public class AppiumBasicTest extends AppiumBase {
public static void main(String[] args) {
AndroidDriver<AndroidElement> androidDriver = initializeAppiumCapabilities();
AndroidElement preferenceText = androidDriver.findElementByXPath("//*[@text='Preference']");
preferenceText.click();
AndroidElement preferenceDependenciesText = androidDriver
.findElementByXPath("//android.widget.TextView[@text='3. Preference dependencies']");
preferenceDependenciesText.click();
AndroidElement wifiCheckbox = androidDriver.findElementById("android:id/checkbox");
wifiCheckbox.click();
}
}
To run this test, perform the following steps one by one:
1) Start the Appium server by running the command appium in command prompt. The Appium server should be kept running throughout your Appium test execution. Once it starts running, it will look like image below.
2) To start the emulator (virtual device), open another command prompt and go inside the emulator folder present inside the Android SDK folder and enter the command emulator -avd deviceName (for me the command looks like emulator -avd Nexus_5X_Edited_API_28). Only one virtual device should be running at a time and to verify that, use the command adb devices which should display only one device.
Keep your emulator open on the screen and run the code as “Java Application”. You should be able to view the logs getting generated in the Appium Server as the test starts to run. Initially, the test might take some time to run. But if every setup is done correctly, then the ApiDemos-debug app will get installed in your virtual device and the tests will run successfully on that app. Images below.
CONGRATULATIONS!! You have successfully run your first test using Appium.
NOTE:
You can also invoke an app from the mobile device itself without downloading the apk file. To do that you need to know the package name and the activity name of the app and then use the below code.
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, MobilePlatform.ANDROID);
desiredCapabilities.setCapability(MobileCapabilityType.DEVICE_NAME, "Nexus_5X_Edited_API_28");
desiredCapabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, "uiautomator2");
desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, "<insert app package name here>");
desiredCapabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, "<insert app activity name here>");
APPIUM LOCATORS
Whenever we perform automation on a mobile application or on a desktop/ mobile web browser, we basically perform 2 activities:
1) We locate the elements using various Locator techniques.
2) We perform some Action on the located element(s) like click, entering text, extracting text, drag and drop etc.
Since, Appium speaks the WebDriver protocol, it is able to support a subset of Selenium WebDriver Locator Strategies along with some additional Mobile JSON Wire Protocol Locator Strategies.
WebDriver Locator Strategies:
Class name (For Android this is the native UI class (not HTML class) e.g. android.widget.Button, for iOS this is the full name of the XCUI element and begins with XCUIElementType).
ID (For Android this is the native resource-id attribute and for iOS this is the name attribute)
XPath (Search of the app XML source document, representing a native UI hierarchy, using the given path)
JSON Wire Protocol Locator Strategies:
Accessibility ID
This is an unique element identifier – a string used for making a UI object accessible. For Android this is the content-desc attribute, for iOS this is the accessibility-id attribute.
Android UiAutomator (for UiAutomator2 only)
Sends the java code as a String to the Appium server using the Android UiAutomator selector API, usually the UiSelector class, which then executes in the application environment and sends back the element(s)
Additional Experimental Locator Strategies
Image
Locate an element by matching it with a Base64 encoded image file that is send to the Appium Server. If Appium can find a screen region matching the template, it will wrap up information about this region as a standard WebElement and send it back to the Appium client
Custom
String send to an element finding plugin registered via the customFindModules capability.
For more information on the Additional Experimental Locator Strategies, you can visit the Appium official webpage.
Let’s now see one by one how we can use the locator strategies to find the elements. To view the elements, you can use “uiautomatorviewer” batch file present inside the Android SDK tools bin folder (e.g. C:\Users\Sumon\AppData\Local\Android\Sdk\tools\bin). First ensure that your virtual device (emulator) is up and running. Then doubleclick on uiautomatorviewer file. Once the uiautomator opens up, click on the second icon from top to take screenshot from the device. You will be able to see all the elements. You can then click on each element to view its attributes and values.
1) CLASS NAME
To locate an element in Android using the class name, first we need to identify the classname which is the full name of the UiAutomator2 class e.g. android.view.View, android.widget.TextView, android.widget.Button etc. Also, remember that classname value will be the tagname while writing xpath expression (which we will see below). To locate an element using the classname we can use either of the following lines of code:
AndroidElement nextButton=driver.findElementByClassName("android.widget.Button");
AndroidElement nextButton=driver.findElement(By.className(“android.widget.Button”));
In most of the cases, multiple elements will be having the same classname and if we use findElement it will return the first matched element. To get all the elements and iterate over them, we need to use findElementsByClassName and findElements methods.
2) ID
“ID” is the unique native element identifier for mobile applications. For android, this is resource-id attribute and for iOS, this is the name attribute. To locate an element with resource-id we can use either of the below lines of code:
AndroidElement wifiCheckbox=androidDriver.findElementById("android:id/checkbox");
AndroidElement wifiCheckbox=androidDriver.findElement(By.id("android:id/checkbox"));
3) XPATH
XPath is an abstract representation of a path to an element. When we use xpath expression to find an element, it searches the application XML source containing the hierarchical relationship between the elements. Thus, it might take some time to find the elements. Hence, the above locating strategies should be preferred first before deciding to use Xpath. To use Xpath, we need to use class name as tagname, followed by the attribute(s) and attribute value(s).
AndroidElement advancedPreferencesText=androidDriver.findElementByXPath("//android.widget.TextView[@text=‘6. Advanced preferences']");
AndroidElement advancedPreferencesText=androidDriver.findElement(By.xpath("//android.widget.TextView[@text=’6. Advanced preferences']"));
Here, android.widget.TextView is the class name (tag name)
text is the text attribute
6. Advanced preferences is the text attribute value. If we want to consider searching the text attribute in all the elements we need to use * symbol like below:
AndroidElement advancedPreferencesText=androidDriver.findElementByXPath("//*[@text='6. Advanced preferences']");
4) ACCESSIBILITY ID
For Android, the content-desc is a unique element locator attribute that can be used to locate an element.
AndroidElement accessibilityServiceText=androidDriver.findElementByAccessibilityId("Accessibility Service");
5) UiAUTOMATOR
Using the UiAutomator API, we can send Java Code as a String to the Appium Server. The server will then execute the code in the Application environment using the UiAutomator2 driver for Android platform. The response will be the matched element or elements.
AndroidElement graphicsText=driver.findElementByAndroidUIAutomator("new UiScrollable(new UiSelector()).scrollIntoView(text(\"Graphics\"))");
APPIUM GESTURES
To execute automated testing using Appium, after the appropriate locator is found, the next step is to perform action on the element.
Though the webdriver specification provides support for certain kind of mobile interaction, its parameters are not always easily mappable to the functionality of the underlying device automation framework.
Appium implements the TouchAction/MultiAction API which helps to build up gestures with multiple actuators. Following are some actions and gestures which can be used on the elements:
TouchAction
The objects of the TouchAction class consist of a series of events. The events are “press”, “release”, “moveTo”, “tap”, “wait”, “longPress”, “cancel” and “perform”. The events take place in a sequence.
A wait event can also be added in between these events to control the timing of the gesture.
The tap event is to tap to an element on the mobile device.
TouchAction touchAction = new TouchAction(driver);
AndroidElement expandableListsText = driver
.findElementByXPath("//android.widget.TextView[@text='Expandable Lists']");
touchAction.tap(tapOptions().withElement(element(expandableListsText))).perform();
The press and longPress events are self-explanatory. These are used to press on an element or press on an element for a longer time duration respectively. To release the press, release event is used.
TouchAction touchAction = new TouchAction(driver);
AndroidElement peopleNamesText = driver
.findElementByXPath("//android.widget.TextView[@text='People Names']");
touchAction.longPress(longPressOptions().withElement(element(peopleNamesText)).withDuration(ofSeconds(5)))
.release().perform();
The perform event sends the entire sequence of events to Appium and then the gesture is run on the device.
The moveTo event is used to move to an element or to a particular position defined by coordinates.
TouchAction touchAction = new TouchAction(driver);
AndroidElement hello1Text = driver.findElementByXPath("//*[@content-desc='hello1']");
AndroidElement hello2Text = driver.findElementByXPath("//*[@content-desc='hello2']");
touchAction.longPress(longPressOptions().withElement(element(hello1Text)).withDuration(ofSeconds(2)))
.moveTo(element(hello2Text)).release().perform();
MultiTouch
The objects of MultiTouch are collections of TouchActions. The Multitouch gestures only have 2 methods: add and perform.
“add” is used to add another TouchAction to the MultiTouch. Once “perform” is called, all the TouchActions added to the MultiTouch are sent to Appium and performed as if they happened at the same time. Appium first performs the first event of all TouchActions together and then the second.
TouchActions actionOne = new TouchAction();
actionOne.press(10, 10);
actionOne.moveTo(10, 100);
actionOne.release();
TouchActions actionTwo = new TouchAction();
actionTwo.press(20, 20);
actionTwo.moveTo(20, 200);
actionTwo.release();
MultiTouchAction action = new MultiTouchAction();
action.add(actionOne);
action.add(actionTwo);
action.perform();
Credits:
https://appium.io/