mobile
Description
As the name suggests, this is a collection of commands to support mobile automation, with mobile testing as the common form. Generally speaking, there are 3 strategies for mobile testing:
- via emulators
- via real devices connected to the automation host
- via cloud testing provider (such as BrowserStack or CrossBrowserTesting)
Nexial intends to support all 3 types of mobile testing for both iOS and Android devices. However, the availability of these capabilities will be rolled out over time.
Nexial internally uses Appium to perform the communication and data transport between your scripts and the target devices. Unless of urgent or critical matter (such as a high priority security patch), Nexial will only ship with the last official stable release of Appium.
To perform mobile automation, here are the steps to follow:
- Install and Configure Appium
- Install Android SDK and Emulators - if you are targeting Android devices
- Install Xcode and iOS Emulators - if you are targeting iOS devices
- Configure Mobile Device Profile
- Scripting with Emulator
- [Run your Mobile Automation on BrowserStack] - if you are planning to run your test via BrowserStack COMING SOON!
Click on each of the links above to get started.
Locators
Those who are familiar with Web Automation know that there are multiple types of locators (also known as selectors) to identify Web elements and interact with them. For mobile automation, the use of locators is equally critical. However, there are some differences between these two types of automation. For one, not all the locators are available in mobile automation due to the underlying implementation mismatch (CSS, for example). Mobile automation also brings about a few new/unique locators as well.
For more details about using Appium Desktop to inspect elements and find locator, please visit Appium documentation on Finding and interacting with elements.
- ID
- Accessibility ID
- XPath
- Name
- Resource ID
- Predicate
- Class Chain
- Image
- Link Text
- Partial Link Text
- Text
- Nearby
- "One Of"
- Tag
- Style Class
- CSS
id=
Reference an element by its ID is, by far, the simplest type of locator. Unfortunately, just like its web counterpart, the "ID" in reference isn't always unique as one would hope. For simpler applications, this locator usually works well. For more complex applications where an "ID" might be repeated across multiple elements, a different locator strategy (such as XPATH) might be a better way to go.
For Android applications, the "resource-id" property is also considered as an "ID". One can use either the
id=...
or res=...
syntax. Here's an example of finding an "ID" of an element via
the Appium inspector:For the above example, one may express this locator as:
id=com.google.android.calculator:id/digit_5
res=com.google.android.calculator:id/digit_5
id=...
or name=...
syntax.
Nexial will appropriately resolve it to the correct type of locator.For the above example, one may express this locator as:
id=Remind me on a day
name=Remind me on a day
a11y=
The design purpose of an accessibility ID is to improve usability for users with visual or hearing impairments. Accessibility tools like screen readers or TTS software utilize these IDs to provide additional usability support. From the test automation standpoint, the same accessibility ID can be utilized as the basis for identifying an element. As a convenience, Nexial recognizes a locator by accessibility ID via the
a11y
prefix (there are 11 characters between a
and y
of
accessibility
).On Android, this locator strategy uses
contentDescription
or content-desc
property:For the above example, the locator would be
a11y=Dark theme
.On iOS, the accessibility id, the id, and the name locator strategies are all identical. Behind the scene, they are implemented the same way.
For the above example, one would specify this locator as
a11y=Back
.Generally speaking, it is preferred to use the accessibility ID over ID (although on iOS they are the same) because the accessibility ID is:
- beneficial to both testers and users with visual or hearing impairment.
- generally more readable than plain ID.
xpath=
or without any prefixes.The tried and true XPATH locator is available for mobile automation, as in the case of web or desktop automation. And as it is in the case of other automation uses, XPATH is both powerful and potentially troublesome.
With XPATH, one can search for an element via its hierarchy, its attributes, and its text. Whereas the power lies, so is the danger thereof ( Peter Parker principle).
It is often prudent NOT to use the XPATH locator provided by the Appium inspector. Instead, one should put some effort into simplifying it. For example,
The provided/suggested XPATH for the "Connected devices" link is as follows (broken into lines for readability):
/hierarchy/android.widget.FrameLayout /android.widget.LinearLayout /android.widget.FrameLayout /android.view.ViewGroup /android.widget.ScrollView /android.widget.LinearLayout /android.widget.FrameLayout[2] /android.widget.LinearLayout /android.widget.FrameLayout /androidx.recyclerview.widget.RecyclerView /android.widget.LinearLayout[2] /android.widget.RelativeLayout /android.widget.TextView[1]Surely this would work (since Appium provides this). It is also incredibly difficult to decipher and to maintain. Instead, one could reduce this to something like:
//android.widget.TextView[@text='Connected devices']
//*[@text='Connected devices']
- Easier to read, hence easier to maintain
- No longer dependent on the hierarchy or placements of UI elements
- Potentially faster than the long-winded version
xpath=
.
name=
Only applicable for iOS automation. Alias to the ID locator. See ID locator for more details.
res=
Only applicable for Android automation. Alias to the ID locator. See ID locator for more details.
predicate=
Only applicable for iOS automation. Also known as
-ios predicate string
. This type
of locator represents a recursive element search using the iOS Predicate (iOS 10.0 and above). The syntax
is similar to the XPATH query but comes with its own set of logical and comparative operators. While this type
of locator is powerful, one would essentially be writing another "mini" query language. Consult the
following links for more details:
- https://appium.io/docs/en/writing-running-appium/ios/ios-predicate/
- https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios/ios-predicate.md
- https://developer.apple.com/documentation/foundation/nspredicate
For this type of locator, prefix it with
predicate=
.
cc=
Only applicable for iOS automation. This type of locator strategy is designed to replace XPATH (albeit only available on iOS platform) to quickly locate hierarchical elements. It is possible to use class chain (from this point,
cc
) to search for direct child element using /
or
search for descendent elements using **/
syntax. Filter criteria should be enclosed within
back tick (`...`
) for matching predicate, and enclosed within dollar ($...$
) for
containing predicates. For example, the following XPATH://XCUIElementTypeTable[@name="table"]/XCUIElementTypeCell[@visible="true" and .//XCUIElementTypeTextField[@name="input]]... would be translated into
**/XCUIElementTypeTable[`name == "table"`]/XCUIElementTypeCell[`visible == 1`][$type == "XCUIElementTypeTextField" AND name == "input"$] \_____________________/\_________________/\__________________/\______________/\________________________________________________________/ find an "XCUIElementTypeTable" that has an element... attribute named "name" with value equals to "table" followed by an "XCUIElementTypeCell" cell... that is visible and it contains under its hierarchy another element of type "XCUIElementTypeTextField" which has an attribute called "name" with value equals to "input".
While not necessarily less verbose than its XPATH counterpart,
cc
locator promised to be more
performant without losing much of the flexibility found in XPATH. Here are a few more examples,-
XCUIElementTypeWindow[2]
- selects the second window in the hierarchy. -
XCUIElementTypeWindow[label BEGINSWITH “foo”][-1]
- selects the last window whose label begins withfoo
. -
**/XCUIElementTypeCell[name BEGINSWITH "C"]/XCUIElementTypeButton[10]
- selects the 10th child button of the first cell in the tree whose name starts withC
and which has at least ten direct children of typeXCUIElementTypeButton
.
cc
locator for an iOS device.For more details about the class chain queries, please visit:
clickByDisplayText(text)
to achieve the same effect.
Note
that this command supports PolyMatcher, a flexible way to qualify the click target via its text. See the
command for more details.
clickByDisplayText(text)
to achieve the same effect.
Note
that this command supports PolyMatcher, a flexible way to qualify the click target via its text. See the
command for more details.
text=
This is a Nexial-only addition to the world of locators. The central idea here is to simplify automation by providing a simple way to reference an element that contains a unique string of text within the current application screen. For example,
text=Enter your first name here... references an element that contains an attribute named
text
with the value of
Enter your first name here
.In situations where one needs to automate a form on a mobile app, this type of locator might prove to be most useful. It is both easy to use and to decipher. This form of locator also supports PolyMatcher, which provides further expressiveness and flexibility. For example, to reference a component whose text starts with
Welcome home
, one can use the following text locator:text=START:Welcome homeFor more details on PolyMatcher, visit
assertTextPresent(locator,text)
and click on
"PolyMatcher Enabled" link.Note that behind the scene, Nexial converts such locator to an XPATH-based locator.
nearby=
This is a specialized locator (only in Nexial!) aimed to simplify the task of creating a locator. Instead of identifying an element via its attributes such as
content-desc
, id
, or
accessibility id
, the nearby
locator allows one to identify an element by its
surrounding. Nexial takes care of translating to the appropriate reference during execution time and thus
saving time during scripting. While this technique does not yield the same benefit in every scenario, it
is often a great time saver and a significant reduction in the cost of maintaining automation artifacts.Let's consider the following example:
To identity the text box, we would need to inspect its attributes (typically via Appium Inspector). With the
nearby
locator, we can simply specify it as: nearby={below:Cellphone}
(reads:
Locate the element that is immediately below the element with text Cellphone
). This
simplified form is both easy to read and maintain.Here's another example:
The
nearby
locator to identify the "Male" radio button is simply
nearby={left-of:Male}
.In the above example, the radio buttons do not possess any visible text. Typically, one would need to construct suitable locators using these buttons' other attributes - likely with the help of Appium Inspector (or equivalent). With the
nearby
locator, we can use the "visible" surrounding of
the element instead.Here's the syntax of this
nearby
locator:nearby={[relative-to]:[visible-text]}{item:[index]}{attribute=value,attribute=value,...}where,
[relative-to]
|
One of these values:
|
---|---|
[visible-text]
|
The visible text is used to identify the target element. |
item:...
|
In situations where there are multiple matches, one can specify the element index (as in, the
nth element) via the item keyword. The index must be a positive integer
(0-based), or the special keyword last . Use item:last to signify the last
match item.Consider the following example: nearby={right-of:UNCHECKED}{index:1} would reference the left-most checkbox and
nearby={right-of:UNCHECKED}{index:last} would reference the last (right-most) checkbox.
|
attribute=value,...
|
OPTIONAL. One can supply additional attributes of the target element to improve the locator precision. This is generally not needed. At times when there are many elements nearby the same text element, one may want to consider adding additional attributes to filter out the conflicting elements. |
image=
Coming soon, stay tuned!!
one-of=
This is yet another Nexial-only, specialized locator aiming at simplifying platform-specific locator management. The main design idea is to allow one to specify multiple locators - some for a specific platform, while some as generalized locators - and allow Nexial to pick the best locator at runtime. With the
one-of
locator (more like locators), the multiple locators specified are filtered
at execution time to the appropriate ones. This allows for cleaner automation scripts and a simpler way to
maintain platform-specific or even build-specific locators. Here's an example to demonstrate this type of
locator:one-of={text=Clear all}{android;id=dismiss_text}{ios;id=clear_all_button}The above read:
First, look for a component whose text is "Clear all".The multiple decision points here are neatly synthesized into a series of
If that can't be found and the current device is Android, then look for a component with an ID of "dismiss_text".
However, if the current device is iOS, then look for a component with an ID of "clear_all_button".
{...}{...}
. This
simplifies the automation effort since we wouldn't need to maintain device-specific scripts or
device-specific locators.It is noteworthy to state that the order of these "inner" locators is important. Nexial will filter out all the irrelevant ones (
android
for iOS and ios
for Android) and "test" the relevant
ones from left to right. It is generally a good idea to order the most likely "inner" locator ahead of the
other ones.Here's the syntax of this
one-of
locator:one-of={[android|ios];[locator]}{[locator]}{[locator]}...where,
[android|ios];
|
android or ios - each specified locator may be prefixed with
android or ios to signify its platform/device dependency. During
execution, Nexial will filter out the irrelevant locator(s) based on this. Note that this must be
suffixed with a semi-colon. For example: {android;id=FirstName}
|
---|---|
[locator]
|
The device-specific or generalized locator. One may choose from any of the supported locators such as ID, Accessibility ID, Text, etc. |
Available Commands
In almost all cases, one would start with the use(profile)
command to establish connectivity between
Nexial, Appium server, and the target device. Without proper connectivity, none of the “mobile” commands would run
correctly. When finished, use the closeApp()
command or shutdown(profile)
to
close down the application configured for a profile
. One then can switch to another profile via the
use(profile)
command – potentially to connect to another device or another Appium server.
If the Appium server is configured to start internally within Nexial, one can also terminate it via the
shutdown(profile)
command.
Click on one of the links below to learn about each command.
assertAlertPresent(text)
assertCount(locator,count)
assertElementDisabled(locator)
assertElementEnabled(locator)
assertElementNotPresent(locator)
assertElementNotVisible(locator)
assertElementPresent(locator)
assertElementVisible(locator)
assertElementsPresent(prefix)
assertLocked()
assertTextPresent(locator,text)
back()
clearAlert(option)
clearNotification()
click(locator)
clickByDisplayText(text)
clickUntilNotFound(locator,waitMs,max)
closeApp()
copyToLocal(file,folder)
doubleClick(locator,xOffset,yOffset)
forward()
hideKeyboard()
home()
launchApp(app)
lock()
longClick(locator,waitMs)
orientation(mode)
recentApps
saveAlertText(var)
saveAttributes(var,locator,attribute)
saveCount(var,locator)
saveLockStatus(var)
saveText(var,locator)
saveTextArray(var,locator)
screenshot(file,locator)
scroll(locator,direction)
scrollUntilFound(scrollTarget,direction,searchFor,maxWaitMs)
select(locator,item)
sendSms(phone,message)
shake()
shutdown(profile)
slide(start,end)
type(locator,text)
unlock()
use(profile)
waitForElementPresent(locator,waitMs)
zoom(start1,end1,start2,end2)