Please select the desired Automated Testing Framework:
Table of Contents
Appium Overview
Appium was originally developed by Dan Cueller as a way to take advantage of the UIAutomation framework for Apple iOS to run tests against native mobile applications. Using the same syntax as Selenium, it shares similarities with Selenium's ability to automate interaction with a website through a mobile browser. Although Appium can test websites on a mobile device, it is more commonly used for testing native and hybrid mobile applications for both iOS and Android.
Appium Architecture
Appium has a client-server architecture.
Appium Client
- includes a set of client libraries for various scripting languages in which you write your test scripts based on the Selenium WebDriver API.
Appium Server
- includes a server component, based on node.js, which exposes the WebDriver API. In fact it exposes a superset of the WebDriver API known as the Mobile JSON Wire Protocol.
includes a desktop application, available for both OS X and Windows, that includes everything you need to run Appium bundled in a single package, as well as the ability to inspect elements in running applications.
Appium Desktop Support
The Appium Desktop is currently unsupported by the Appium core team.
The Seven Basic Steps of Testing with Appium
There are seven basic steps in creating an Appium test script for your application under test (AUT):
- Set the location of the application (e.g. Sauce Storage, AWS, GitHub, etc.)
- Create an Appium driver instance which points to a running Appium server (e.g. the servers on Sauce Labs).
- Locate an element within the application (e.g. a login button or link).
- Perform an action on the element (e.g. tap the login button).
- Anticipate the application response to the action (e.g. successfully logged in)
- Run tests and record test results. (e.g. log whether test passed, failed, or returned an error)
- Conclude the test (e.g. shut down connection to Sauce Labs)
Locating the Mobile Application
When you write an Appium test script, the most basic component is the DesiredCapabilities
object, which sets the parameters of your test, such as the mobile platform and operating system you want to test against. Within that object, one of the required capabilities is Application Path, or the app
desired capability. One of the advantages of the Appium architecture is that the application you want to test can be hosted anywhere, from a local path to any other web host on the network, since the Appium server will send the commands it receives from the client to any application path you specify. Practically, you have three options.
Below are the various ways you can upload your application for automated "emusim" (emulators and simulators) tests:
Uploading to Sauce Application Storage
Below are some examples on how to upload your application using the Storage API /upload
method. Supported application file types include *.APK, *IPA, or *.ZIP (.ZIP must include a valid iOS application bundle) files of up to 4 GB. In addition to the application itself, you can upload test assets (e.g., e.g., *.js, *.py, *.tar, *.zip, *.sh, *.bat) for use in your automated tests, such as pre-run executables. For more details about the Storage API, please visit Application Storage.
Uploading with cURL
If you're using cURL for the upload, you must include the @
before your file path, e.g. payload=@path/to/your_file_name
$ curl -F 'payload=@/Users/<user-name>/Downloads/<file_name>.apk' -F name=<file_name>.apk -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" 'https://api.us-west-1.saucelabs.com/v1/storage/upload'
> curl -F 'payload=@\Users\<user-name>\Downloads\<file_name>.apk' -F name=<file_name>.apk -u "%SAUCE_USERNAME%:%SAUCE_ACCESS_KEY%" 'https://api.us-west-1.saucelabs.com/v1/storage/upload'
Then you need to reference a unique identifier (id
) with the "app"
capability in order to use the package file in your automated tests ("app":"<uuid>")
. For more information visit the following section in the Application Storage page: Using Application Storage with Automated Test Builds.
Uploading to a Remote Location
There may be situations where you want to install an application from a downloadable remote location (AWS S3 bucket, a GitHub repository, etc.).
Please review the following guidelines below before uploading your application:
- Make sure your application meets the prerequisite requirements for Android and iOS Mobile Application Testing.
- Upload your application to the hosting location.
- Ensure Sauce Labs has READ access to the app URL.
In your test script, enter the URL for the application as the
"app"
desired capability. Below are some example snippets:caps.setCapability("app", "https://github.com/saucelabs/sample-app-mobile/releases/download/2.2.1/iOS.Simulator.SauceLabs.Mobile.Sample.app.2.1.1.zip);
caps.SetCapability("app", "https://github.com/saucelabs/sample-app-mobile/releases/download/2.2.1/iOS.Simulator.SauceLabs.Mobile.Sample.app.2.1.1.zip");
app: 'https://github.com/saucelabs/sample-app-mobile/releases/download/2.2.1/iOS.Simulator.SauceLabs.Mobile.Sample.app.2.1.1.zip',
'app': 'https://github.com/saucelabs/sample-app-mobile/releases/download/2.2.1/iOS.Simulator.SauceLabs.Mobile.Sample.app.2.1.1.zip',
app: 'https://github.com/saucelabs/sample-app-mobile/releases/download/2.2.1/iOS.Simulator.SauceLabs.Mobile.Sample.app.2.1.1.zip'
Uploading to Legacy Sauce Storage
Sauce Storage is a private temporary storage space. Files uploaded will expire seven days after upload, and be removed. If it has been over seven days since you uploaded your application to Sauce Storage, you will need to upload it again. You can upload the application you want to test to Sauce Storage using our REST API, and then access it for testing by specifying sauce-storage:myapp
for the app
capability in your test script. You upload apps using the upload_file
method of the Sauce Labs REST API.
You can use any REST client; cURL is a convenient command-line option.
US-WEST Data Center
$ curl -u $SAUCE_USERNAME:$SAUCE_ACCESS_KEY -X POST -H "Content-Type: application/octet-stream" \ "https://saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/$APP_NAME?overwrite=true" --data-binary @path/to/your_file_name
US-EAST Data Center
$ curl -u $SAUCE_USERNAME:$SAUCE_ACCESS_KEY -X POST -H "Content-Type: application/octet-stream" \ "https://us-east-1.saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/$APP_NAME?overwrite=true" --data-binary @path/to/your_file_name
EU-CENTRAL Data Center
$ curl -u $SAUCE_USERNAME:$SAUCE_ACCESS_KEY -X POST -H "Content-Type: application/octet-stream" \ "https://eu-central-1.saucelabs.com/rest/v1/storage/$SAUCE_USERNAME/$APP_NAME?overwrite=true" --data-binary @path/to/your_file_name
US-WEST Data Center
> curl -u %SAUCE_USERNAME%:%SAUCE_ACCESS_KEY% -X POST -H "Content-Type: application/octet-stream" \ "https://saucelabs.com/rest/v1/storage/%SAUCE_USERNAME%/%APP_NAME%?overwrite=true" --data-binary @path\to\your_file_name
US-EAST Data Center
> curl -u %SAUCE_USERNAME%:%SAUCE_ACCESS_KEY% -X POST -H "Content-Type: application/octet-stream" \ "https://us-east-1.saucelabs.com/rest/v1/storage/%SAUCE_USERNAME%/%APP_NAME%?overwrite=true" --data-binary @path\to\your_file_name
EU-CENTRAL Data Center
> curl -u %SAUCE_USERNAME%:%SAUCE_ACCESS_KEY% -X POST -H "Content-Type: application/octet-stream" \ "https://eu-central-1.saucelabs.com/rest/v1/storage/%SAUCE_USERNAME%/%APP_NAME%?overwrite=true" --data-binary @path\to\your_file_name
Creating a WebDriver Instance
The WebDriver
instance is the starting point for all uses of the Mobile JSON Wire Protocol.
You create an instance of the WebDriver
interface using a constructor for either Android or iOS. For mobile native application tests, you set both the platform and browser to test against by setting the browserName
desired capability.
Once you have created an instance of the WebDriver
interface, you use this instance to invoke methods, such as tap and swipe, to access other interfaces used in basic test steps. You do so by assigning the instance to a variable when you create it, and by using that variable to invoke methods.
WebDriver Examples
These psuedo-code examples illustrate how to instantiate iOS and Android WebDriver
objects in the various Appium language bindings.
Visit the java-client page for more information on the Java Appium language bindings.
WebDriver driver = new iOSDriver<WebElement>( new URL("https://ondemand.saucelabs.com/wd/hub"), capabilities);
WebDriver driver = new AndroidDriver<WebElement>( new URL("https://ondemand.saucelabs.com/wd/hub"), capabilities);
Visit the python-client page for more information on the Python Appium language bindings.
desired_caps = {} desired_caps['platformName'] = 'iOS' desired_caps['app'] = PATH('../../apps/MyIOS.app.zip') self.driver = webdriver.Remote('https://ondemand.saucelabs.com/wd/hub', desired_caps)
desired_caps = {} desired_caps['platformName'] = 'Android' desired_caps['app'] = PATH('../../apps/MyIOS.apk') self.driver = webdriver.Remote('https://ondemand.saucelabs.com/wd/hub', desired_caps)
We recommend using the webdriverio testing utility as your WebDriver testing framework for node.js. For more information, refer to the documentation.
const opts = { capabilities: { platformName: "iOS", app: "/path/to/the/downloaded/MyiOS.app.zip", } }; const client = wdio.remote(opts);
const opts = { capabilities: { platformName: "Android", app: "/path/to/the/downloaded/MyAndroid.apk", } }; const client = wdio.remote(opts);
Visit appium_lib for more information about the Ruby Appium language bindings.
opts = { caps: { platformName: :ios, app: '/path/to/MyiOS.app'}, appium_lib: { wait_timeout: 30 }} appium_driver = Appium::Driver.new(opts, true) appium_driver.start_driver
opts = { caps: { platformName: :android, app: '/path/to/MyAndroid.apk'}, appium_lib: { wait_timeout: 30 }} appium_driver = Appium::Driver.new(opts, true) appium_driver.start_driver
Visit the appium-dot-net page for more information about the C# Appium language bindings.
var sauceURL = "https://ondemand.saucelabs.com/wd/hub"; var driver = new IOSDriver<IWebElement>(new Uri(sauceURL));
var sauceURL = "https://ondemand.saucelabs.com/wd/hub"; var driver = new AndroidDriver<IWebElement>(new Uri(sauceURL));
Locating Application Elements
In order to find elements in a mobile environment, Appium implements a number of locator strategies that are specific to, or adaptations for, the particulars of a mobile device. Three are available for both Android and iOS:
The accessibility id
locator strategy is designed to read a unique identifier for a UI element. This has the benefit of not changing during localization or any other process that might change text. In addition, it can be an aid in creating cross-platform tests, if elements that are functionally the same have the same accessibility id.
For iOS this is the
accessibility identifier
laid out by Apple here.For Android the
accessibility id
maps to thecontent-description
for the element, as described here.
For both platforms getting an element, or multiple elements, by their accessibility id
is usually the best method. It is also the preferred way, in replacement of the deprecated name
strategy.
The client libraries specific to Appium support getting elements by accessibility id
.
#python example driver.find_element_by_accessibility_id('my_accessibility_identifier')
The class name
strategy is a string
representing a UI element on the current view.
- For iOS it is the full name of a UIAutomation class, and will begin with
UIA
-, such asUIATextField
for a text field. A full reference can be found here. - For Android it is the fully qualified name of a UI Automator class, such
android.widget.EditText
for a text field. A full reference can be found here.
The client libraries for Appium support getting a single element, or multiple elements, based on the class name
. This functionality is in the Selenium clients (e.g., Python).
#python example driver.find_element_by_class_name('android.widget.DatePicker')
In the mobile environment, id
s are not, as in WebDriver, CSS ids, but rather some form of native identifier.
- For iOS the situation is complicated. Appium will first search for an
accessibility id
that matches. If there is none found, a string match will be attempted on the element labels. Finally, if the id passed in is a localization key, it will search the localized string. - For Android, the
id
is the element’sandroid:id
.
Example: Locate elements for username and password
This example invokes the findElement
method on the driver
variable, using the name
attribute to locate the username
and password
text input elements, and (optionally) the id
attribute to locate the form
element.
#java example import org.openqa.selenium.By; import org.openqa.selenium.WebElement; WebElement emailInput = driver.findElement(By.id("fbemail"));
The xpath
locator strategy is also available in the WebDriver protocol, and exposes the functionality of XPath language to locate elements within a mobile view. An XML representation of the view is created in Appium, and searches are made against that image.
The Selenium clients have methods for retrieving elements using the xpath
locator strategy.
#python example driver.find_element_by_xpath('//UIAApplication[1]/UIAWindow[1]/UIATextField[1]')
Best Practices for Identifying Application Elements
It is always best to use an element locator that uniquely identifies the element, like an id or an accessibility id. Class names and xpath are best used only when IDs are not available. Multiple elements can have the same class name, and using xpath searches through the entire markup to find the element, which can slow down your tests.
Performing Actions on the Application Elements
Once you've identified the mobile elements you want your test to interact with, the next step is to interact with them. You perform an action on a mobile element by invoking an interaction method on an instance of the WebElement
interface.
The WebElement
interface declares basic interaction methods including:
- The
sendKeys
method, to enter text - The
clear
method, to clear entered text - The
submit
method, to submit a form
Example
This example first invokes the sendKeys
method to enter text in the username
and password
elements, and then invokes the submit
method to submit the login
form.
emailInput.sendKeys("SauceIsAwesome@email.com");
Submit the form
The submit
method can be invoked either on any text input element on a form, or on the form element itself.
emailInput.submit();
Anticipating the Application Response
When you click a Submit button, you know that you have to wait a second or two for your action to reach the server, and for the server to respond, before you do anything else. If you're trying to test the response, and what happens afterwards, then you need to build that waiting time into your test. Otherwise, the test might fail because the elements that are expected for the next step haven't loaded into the browser you. The WebDriver API supports two basic techniques for anticipating browser response by waiting: implicit waits and explicit waits .
Do Not Mix Explicit and Implicit Waits
Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times. For example setting an implicit wait of 10s and an explicit wait of 15 seconds, could cause a timeout to occur after 20 seconds.
Wait Strategies
Implicit waits set a maximum time that the Appium server will continue trying to find an element. Using implicit waits is not a best practice because application response times are not definitely predictable and fixed elapsed times are not applicable to all interactions. Using explicit waits requires more technical sophistication, but is a Sauce Labs best practice.
This example code illustrates how you could use an implicit wait to anticipate web browser response after submitting the login form.
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
Explicit waits wait until an expected condition occurs on the web page, or until a maximum wait time elapses. To use an explicit wait, you create an instance of the WebDriverWait
class with a maximum wait time, and you invoke its until
method with an expected condition.
The WebDriver API provides an ExpectedConditions
class with methods for various standard types of expected condition. These methods return an instance of an expected condition
class. You can pass an invocation of these standard expected-condition methods as argument values to until
method. You can also pass - in ways that your programming language and its WebDriver API support - any function, code block, or closure that returns a boolean value or an object reference to a found web element as an argument value to the until
method. How this is done varies over programming languages. The until
method checks repeatedly, until the maximum wait time elapses, for a true
boolean return value or a non-null
object reference, as an indication that the expected condition has occurred.
This example code illustrates how you could use an explicit wait to anticipate web browser response after submitting the login form.
import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; WebDriverWait wait = new WebDriverWait(driver, 10); WebElement messageElement = wait.until( ExpectedConditions.presenceOfElementLocated(By.id("loginResponse")) );
Running Tests and Recording Results
Running tests and recording test results is the ultimate purpose of your test script: you run tests in an automated test script in order to evaluate function and performance in the AUT, without requiring human interaction.
Test Frameworks
To run test and to record test results, you use methods of a test framework for your programming language. There are many available test frameworks, including the frameworks in the so-called XUnitfamily, which includes:
- JUnit for Java
- NUnit for C#
- unittest or pyunit for Python
- RSpec for Ruby
For some programming languages, test frameworks other than those in the XUnit family are common - for example, the RSpec framework for Ruby. The Sauce Labs sample test framework repos on GitHub contain over 60 examples of test frameworks set up to work with Sauce Labs.
Test Assertions
Most test frameworks implement the basic concept of an assertion, a method representing whether or not a logical condition holds after interaction with an AUT. Test frameworks generally declare methods whose names begin with the term assert
and end with a term for a logical condition, e.g. assertEquals
in JUnit. Generally, when the logical condition represented by an assert
method does not hold, an exception for the condition is thrown. There are various approaches to using exceptions in most test frameworks. The SeleniumHQ documentation has more detailed information on using both assertions and verifications in your tests.
Recording Test Results
Recording of test results can be done in various ways, supported by the test framework or by a logging framework for the programming language, or by both together. Selenium also supports taking screenshots of web browser windows as a helpful additional type of recording. Because of the wide variations in recording technique, this beginning section omits recording, instead emphasizing a simple approach to applying a test using an assert
method. The scripts in Sauce Labs Demonstration Scripts include examples of setting up reporting of test results to Sauce Labs, as do the framework scripts in the Sauce Labs sample test framework repos.
Example
The following example runs a test by asserting that the login response message is equal to an expected success message:
import junit.framework.Assert; import junit.framework.TestCase; WebElement messageElement = driver.findElement(By.id("loginResponse")); String message = messageElement.getText(); String successMsg = "Welcome to foo. You logged in successfully."; assertEquals (message, successMsg);
Concluding the Tests
The quit
Method
You conclude a test by invoking the quit
method on an instance of the WebDriver
interface, e.g. on the driver
variable.
The quit
method concludes a test by disposing of resources, which allows later tests to run without resources and application state affected by earlier tests. The quit
method:
- quits the web browser application, closing all web pages
- quits the WebDriver server, which interacts with the web browser
- releases
driver
, the variable referencing the unique instance of theWebDriver
interface.
Example
The following example invokes the quit
method on the driver
variable:
driver.quit();
Full Example with All Steps
The following example includes code for all steps. The example also defines a Java test class Example
, and its main
method, so that the code can be run.
package com.yourcompany; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import io.appium.java_client.android.AndroidDriver; import java.net.MalformedURLException; import java.net.URL; import junit.framework.Assert; public class SampleSauceTest { public static void main() throws MalformedURLException { desiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability("platformName", "Android"); capabilities.setCapability("deviceName", "Android GoogleAPI Emulator"); capabilities.setCapability("platformVersion", "10.0"); capabilities.setCapability("app", "storage:filename=swag-labs.apk"); capabilities.setCapability("browserName", ""); capabilities.setCapability("deviceOrientation", "portrait"); capabilities.setCapability("appiumVersion", "1.16.0"); WebDriver driver = new AndroidDriver<WebElement>( new URL("http://SAUCE_USERNAME:SAUCE_ACCESS_KEY@ondemand.us-west-1.saucelabs.com/wd/hub"), capabilities); WebElement emailInput = driver.findElement(By.id("fbemail")); emailInput.sendKeys("SauceIsAwesome@email.com"); assertEquals(emailInput.getText(), "SauceIsAwesome@email.com"); driver.quit(); } }
Additional References
There are many additional resources available if you want to dive into more detail with Appium and mobile application testing.
- Appium Bootcamp by Dave Haeffner and Matthew Edwards
- http://appium.io
The official Appium website and documentation - https://youtu.be/1J0aXDbjiUE?list=PLSIUOFhnxEiCODb8XQB-RUQ0RGNZ2yW7d
An introduction to Appium presented by Jonathan Lipps of Sauce Labs and the Appium project given at the 2013 Google Test Automation Conference - https://confengine.com/selenium-conf-2015/proposal/1319/the-mobile-json-wire-protocol
A talk on the mobile JSON wire protocol presented by Jonathan Lipps at the 2015 Selenium Conference - http://stackshare.io/sauce-labs/mobile-automation-with-appium-and-sauce-labs
An in-depth tutorial by Jonathan Lipps covering Appium basics using Ruby and Sauce Labs
Coming Soon!
This section of the wiki is under construction. Please check our blog for announcements about when these pages are available. Thank you for your patience.
Coming Soon!
This section of the wiki is under construction. Please check our blog for announcements about when these pages are available. Thank you for your patience.
Coming Soon!
This section of the wiki is under construction. Please check our blog for announcements about when these pages are available. Thank you for your patience.