2015-06-01

Welcome to the first in our new series, Appium + Sauce Labs Bootcamp. This first chapter will cover an overview of Appium and its commands, demonstrated with detailed examples of the Java and Python language bindings.  Later we will follow up with examples in Ruby. This series goes from fundamental concepts to advanced techniques using Appium and Sauce Labs. The difficulty is Beginner->Advanced.

The Sauce <-> Client Relationship

Sauce Labs provides a service consisting of two APIs which operate at different conceptual levels — the Sauce API and the Webdriver API. Both operate as HTTP (secured with SSL/TLS) and encode their data as simple JSON objects:

Commands which control an individual Mobile Device (such as an iOS simulator or an Android emulator) are sent through the Webdriver API (urls which begin with ondemand.saucelabs.com/wd/hub).

Commands which interact with how Sauce Labs stores Tests and Builds eg: “Passing/Failing” are sent through the Sauce API (urls which begin with saucelabs.com/rest/).

When running an automated test script, every command, such as tapping on the screen or typing into an input field, is sent as an HTTP request to an Appium server through the Sauce Labs Webdriver API. Since commands are sent as standard HTTP packets and every language has methods for communicating over the Internet, automated tests can be run and written using any programming language.

Automated test scripts are descriptions of scenarios that users enact when using an app and it would be cumbersome to fill them full of code which assembles HTTP packets and sends them off to specific urls. For this reason, there exist a number of language bindings (sometimes called Appium Clients or drivers since they “drive” a mobile device) which provide a set of methods and functions which handle communication with the Appium servers running in the Sauce cloud.

The Appium Java client may provide a method for pressing the HOME button:

And the Appium Python client may provide a slightly different function:

But both functions generate the same exact HTTP packet which is sent to:

ondemand.saucelabs.com/wd/hub/session/[session Id number]/appium/device/keyevent

Developers and QA specialists who write automated test scripts need never concern themselves with long URLs like the one in the example above; this is the advantage provided by the language bindings.

The commands that Appium accepts are a superset of the standard protocol used by Selenium servers for automating web browsers. In order to mitigate the effort of learning something completely new, the Appium language bindings all extend and modify existing Selenium language bindings. Though in some cases the methods are very different, the documentation for the various Selenium clients is usually applicable to the Appium clients.

Test Configuration

In order to specify the environment that Sauce Labs will set up and use for running your app, you must populate a set of “desired capabilities”. The entire list of capabilities that Appium can understand is specified here, and the set of capabilities that Sauce Labs understands is here.

The three most important capabilities for mobile testing on Sauce with Appium are: platformName, which specifies whether you are testing an “Android” or “iOS” app; platformVersion, which allows you to specify a specific version of the platform (e.g., iOS “7.1” or “8.0”, or Android “4.3” or “4.4”); and deviceName, through which iOS allows you to distinguish “iPhone” from “iPad”, and for Android, allows you to specify the device itself (e.g., “Samsung Galaxy S3″ or “Nexus 7″).

Further, there are two mutually exclusive capabilities for specifying the app under test: app allows you to define the native app, and must be an app you’ve already placed in Sauce Storage or the URL of the app somewhere on the internet; browserName is used for mobile web testing, allowing you to get a specific browser running on the mobile device (e.g., “Safari” or “Chrome”). If you are testing a native app, do not use browserName, and vice versa.

For example, to tell Sauce Labs to use the application we have uploaded with the name my_app.apk, and to run it on an Android emulator running Android 4.4 and emulating a Samsung Galaxy S3, we use the following capabilities:

For an iOS test, with the following capabilities we will be telling Sauce Labs to use the application we have uploaded with the name my_app.app, and run it on the iPhone simulator running iOS 8.1:

The final capability in both cases pertains to Appium itself. With appiumVersion you can use a specific build of the Appium server (in this case, 1.3.4, the latest as of this writing).

Since the configuration of your environment can be complex and getting it right is very important, Sauce Labs provides a tool, the Platform Configurator, which allows you to visually configure the test environment you want, and generates code in your desired language.

Through the desired capabilities you will send to Sauce Labs, you get the environment in which you would like your test to run. From there, you can automate your test scenarios!

Instantiating a Driver

The various Appium language bindings share the concept of providing a driver object. When desired capabilities are sent to Sauce Labs, a new environment is set up and assigned a session identifier. Each driver is associated with a single session identifier and therefore each driver is associated with a single mobile device for the duration of a test.

The different language bindings have slightly different conventions, but they all need to do the following at the beginning of each test:

Store the SAUCE_USERNAME and SAUCE_ACCESS_KEY for accessing the Sauce Cloud

Set http://ondemand.saucelabs.com:80/wd/hub as the endpoint to send Appium commands to

Specify a set of desired capabilities for this test session

Send the desired capabilities to Sauce Labs and begin a new session

The following are samples of instantiating Appium drivers in Java and Python:

Java

Python

At the end of a test, sessions should be explicitly closed:

Java

Python

Simple Commands

There are multiple commands available for the inspection of the elements present on the UI of a device, and interacting with them. So many, in fact, that it can be overwhelming to learn them all at once. (The complete list is a combination of all the API endpoints described in the Selenium Documentation, and the Appium Documentation.)

The first commands to learn are the following:

Finding Elements

find element

find elements

Inspecting Elements

text

location

size

Interacting with Elements

click

send_keys

Finding Elements

In order to perform any meaningful command, one needs a UI element to work with. Appium allows for finding UI elements by a number of means. The preferred method is to find elements by their Accessibility Id. These would be identifiers which app developers manually attach to important elements so that different handicap accessibility interfaces can meaningfully interpret the UI. The Android and iOS platforms both have Accessibility programs (iOS, Android).

Elements can also be found by using the name of their class. On Android devices, these names start with “android.widget.” eg. “android.widget.TextView” and “android.widget.LinearLayout”. On iOS, class names start with “UIA”, eg. “UIATextField” and “UIATableView”.

If multiple elements are found by these commands, only the first is returned. For finding multiple elements a pluralized version of each command exists. These commands return arrays of elements.

One can find elements contained within another element:

The Java client has an alternative method of finding elements which behaves the same but has a slightly different syntax:

These different approaches to finding elements (by class name or by accessibility ID) are called locator strategies. Appium has additional locator strategies for finding elements by id, xpath, and platform specific locators like iOS UIAutomation commands and Android UIAutomator selectors. These will be discussed in a later chapter.

Inspecting Elements

By inspecting the properties of elements visible on the UI, we can detect whether or not the app behaves as expected. We can test for the presence of a popup, look for a user’s name when logged in, check that lists are populated, that images are in the right place, etc.

Whenever a UI element is “found” through appium, the server returns an id, not an object populated with UI properties. Additional functions need to be called in order to get the specific properties of an element.

The “text” command returns the textual contents of the element.

The “location” command returns the current location of the element on the screen, measured in pixels.

The “size” command returns the size of the element on the screen, measured in pixels.

Since these properties are calculated when the command is called, if the element is no longer visible on the UI the command will fail.

Interacting with Elements

By interacting with elements, we simulate the actions of a user, typing into fields, pressing buttons, tapping the screen, and performing touch gestures.

Use the “click” command to simulate tapping on an element:

Use the “send keys” command to type into a text field.

Touch gestures will be discussed in a later chapter.

Discussed above are the basic commands for finding, inspecting, and interacting with UI elements. The examples are in Java and Python. Each language binding follows conventions particular to its developer culture, but they all encompass the same set of commands. When in doubt, check the documentation for a particular language binding.

Python Example

The example assumes you have the environment variables SAUCE_USERNAME and SAUCE_ACCESS_KEY set with your credentials. Without this the test runner will not be able to authenticate your account, and you will not be able to run your test.

The setUp method configures the environment by populating the desired capabilities such that it is requesting an iPhone simulator running iOS 7.1, with a test application that has been uploaded to AWS. In addition, the Sauce Labs specific capabilities of appiumVersion and name have been used, in order to get version 1.3.4 of the server, and to specify a name that will make sense when we look at it in the Sauce Labs test dashboard.

Once the desired capabilities are set up, the driver instance is created by instantiating appium.webdriver.Remote with the desired capabilities and the “Command Executor”, which is the endpoint at Sauce Labs to which the commands will be sent. This will be http://ondemand.saucelabs.com:80/wd/hub authenticated with your username and access key. Furthermore, it sets the implicit wait value, which is the time the server will wait for an element to exist when trying to find it, to a reasonable number of seconds.

The setUp code will be called automatically with each test. Corresponding to this is the tearDown method, which will be called automatically at the end of each test, as long as the setUp method exited normally. Here the test must report back to Sauce Labs so that the dashboard can show the status of the test. Without this, the Sauce Labs would have no way of knowing how the test fared, since it is the test itself, not the automation on the server, that can distinguish between what is expected and what is errant.

Of course, both the setUp and tearDown code can be abstracted away so that it does not exist in every test case. The configuration can also be done dynamically, so that multiple platforms can be tested automatically and in parallel, taking advantage of the real power of Sauce Labs! (Indeed, there is a class in the Python client that does just this! See the here for an example of how it works!) But for the purpose of demonstration, the code is shown here.

The actual test is in the test_ui_computation method, which most test runners can introspect and run. It first finds two texts fields and types numbers into them.

It also asserts that they have the same size, and the first is above the second, in order to demonstrate the sorts of information one can gather about elements after they are retrieved.

The test automation then finds the button, and clicks it, causing the computation of the sum of the two, asserting that the resulting value is what is expected.

To recap, the test runner will run the setUp code, which opens a connection with Sauce Labs and sends in the requested environment through the desired capabilities. The test method is then run, sending commands to Sauce Labs and receiving responses. When the method finishes, whether normally or through an exception, the tearDown method is called, whose job it is to free any resources, and, in this case, to report to Sauce Labs the status of the test. If there are multiple test methods in the class each will go through this process.

Java Example

The example assumes you have the environment variables SAUCE_USERNAME and SAUCE_ACCESS_KEY set with your credentials. Without this the test runner will not be able to authenticate your account, and you will not be able to run your test.

This example not only includes the Appium Java-client using Maven, but also the Sauce Labs Java Helper.

These JUnit rules allow one to get the name of the currently running test and to automatically use the Sauce API to update the status of each test, marking it as “Passed” or “Failed”. Remember that the Webdriver API used by the IOSDriver is only sending commands to the device and is unaware of your motivations. The Sauce API (abstracted by the SauceOnDemandTestWatcher), detects exceptions and assertions thrown during a test and updates the records on Sauce Labs.

The setUp method configures the environment by populating the desired capabilities such that it is requesting an iPhone simulator running iOS 7.1, with a test application that has been uploaded to AWS. In addition, the Sauce Labs specific capabilities of appiumVersion= and name have been used, in order to get version 1.3.4 of the server, and to specify a name that will make sense when we look at it in the Sauce Labs test dashboard.

The platformNameIOSDriver class.

Once the desired capabilities are set up, the driver instance is created by instantiating IOSDriver with the desired capabilities and the URL to which commands will be sent. This will be http://ondemand.saucelabs.com:80/wd/hub, authenticated with your username and access key. Furthermore, it sets the implicit wait value, which is the time the server will wait for an element to exist when trying to find it, to a reasonable amount of time.

The setUp code will be called automatically with each test. Corresponding to this is the tearDown method, which will be called automatically at the end of each test, as long as the setUp method exited normally.

Of course, both the setUp and tearDown code can be abstracted away so that it does not exist in every test case. The configuration can also be done dynamically, so that multiple platforms can be tested automatically and in parallel, taking advantage of the real power of Sauce Labs! But for the purpose of demonstration, the code is shown here.

The actual test is in the testUIComputation method. It first finds two texts fields and types numbers into them.

It also asserts that they have the same size, and the first is above the second, in order to demonstrate the sorts of information one can gather about elements after they are retrieved.

The test automation then finds the button, and clicks it, causing the computation of the sum of the two, asserting that the resulting value is what is expected.

To recap, the test runner runs the setUp code, which opens a connection with Sauce Labs and sends in the requested environment through the desired capabilities. The test method is then run, sending commands to Sauce Labs and receiving responses. When the method finishes, whether normally or through an exception, the tearDown method is called, whose job it is to free any resources, and, the status gets reported to Sauce Labs by means of the SauceOnDemandTestWatcher. If there are multiple test methods in the class each will go through this process.

You can find all the sample code in this chapter, and more sample of Appium tests written in every language in the Appium sample-code repository.

Show more