Table of Contents generated with DocToc
- katalon-mobile-util
Katalon Studio is an IDE that provides a unified way to test UI for mobile and web.
katalon-mobile-util is a library of utilities to make mobile UI testing in Katalon Studio easier.
For detailed usage, view the Javadocs.
To use this katalon-mobile-util library in Katalon Studio tests, it is not required that you build from source.
Place the release artifact jar into your Katalon test project's /Drivers
directory, or follow the Katalon Studio instructions: How to import external library into your automation project.
After installation, make sure you restart Katalon Studio for the library to be loaded correctly.
Although not required, you may build the katalon-mobile-util library from source code. The resulting .jar
will be added to your Katalon Studio test project.
This library requires Katalon Studio version 7.x to be installed.
Building from source requires Apache Maven.
The Katalon Studio jar files are not available via Maven Central and are not packaged with the katalon-mobile-util library, so we can set up a local maven repository to contain the required files:
Run the script to move the jar files from Katalon Studio to the lib
directory:
cd katalon-mobile-util
./scripts/install-dependencies.sh
Build the katalon-mobile-util package:
mvn package
The resulting .jar
file will be created in the katalon-mobile-util library's target
directory.
-
Edit the katalon-mobile-util project's
pom.xml
file to setproject.target.katalon.directory
to your Katalon Studio test project's/Drivers
directory. -
Follow the steps for "Building from source".
-
Build and install the package:
mvn install -U
- The katalon-mobile-util
.jar
file will be placed in your Katalon Studio test project's/Drivers
directory.
This katalon-mobile-util library provides convenience functions for interacting with Katalon Studio and Appium features:
For detailed usage, view the Javadocs.
Provides information about the platform of the device on which the tests are running. This allows a single set of tests to perform branching logic between iOS and Android without the need to create separate test suites.
Add this import statement to your test file:
import com.detroitlabs.katalonmobileutil.device.App
import com.detroitlabs.katalonmobileutil.device.Device
Start the test application on the device. Provide both the iOS and Android files and use the device platform to determine which file to test.
Set removeAppBeforeTest
below to clear the app before running the test.
String androidFile = '~/Downloads/mobile-beta.apk'
String androidAppId = 'com.mycompany.myapp'
App androidApp = new App(androidFile, androidAppId)
String iosFile = '~/Downloads/mobile-beta.app'
String iosAppId = 'com.mycompany.myapp'
App iosApp = new App(iosFile, iosAppId)
boolean removeAppBeforeTest = true // change this to false to keep the app state between tests
Device.startApp([iosApp, androidApp], removeAppBeforeTest)
Prints the platform for the test device, iOS
or Android
println(Device.getDeviceOS())
Determines the platform of the test device and allows for branching logic
if (Device.isIOS()) {
println("This is an iOS device.")
}
if (Device.isAndroid()) {
println("This is an Android device.")
}
Stop and clear the app data from the device:
NOTE: On iOS, app will not be uninstalled, but its data will be cleared.
boolean uninstallApp = true
Device.stopApp(uninstallApp)
The basic elements of Katalon Studio tests are TestObjects. These objects are stored in the Object Repository. To keep things consistently organized, katalon-mobile-util assumes that the following structure will be used to store TestObjects
:
Because iOS and Android TestObject
properties vary slightly, using "iOS Objects" and "Android Objects" folders allows katalon-mobile-util to dynamically switch between the TestObjects
based on the test device platform. Store the TestObject
with the same name for each platform, e.g. Checkout button
and katalon-mobile-util will pick the correct object for the platform.
Add this import statement to your test file:
import com.detroitlabs.katalonmobileutil.testobject.Finder
Find a TestObject
from the Object Repository:
TestObject alert = Finder.findAlert('My Alert')
TestObject button = Finder.findButton('My Button')
TestObject checkbox = Finder.findCheckbox('My Checkbox')
TestObject image = Finder.findImage('My Image')
TestObject label = Finder.findLabel('My Label')
TestObject link = Finder.findLink('My Link')
TestObject segmentedControl = Finder.findSegmentedControl('My Segmented Control')
TestObject switch = Finder.findSwitch('My Switch')
TestObject tab = Finder.findTab('My Tab')
TestObject textField = Finder.findTextField('My Text Field')
If the TestObject
doesn't fall into one of those categories, you can save it in the top-level iOS or Android directory and use findGeneric
to retrieve it:
TestObject genericObject = Finder.findGeneric('My Uncategorized Generic Object')
Once you have the TestObject
, you can interact with it per the normal Katalon MobileBuiltInKeywords
:
MobileBuiltInKeywords.tap(button, timeout)
If you want to store TestObjects
in a non-default directory, you can override the repository locations:
Finder.setIOSRepository('My Custom iOS Folder')
Finder.setAndroidRepository('My Custom Android Folder')
TIP: All of the available TestObject types can be used by importing:
import com.detroitlabs.katalonmobileutil.testobject.TestObjectType
// Gives you access to:
TestObjectType.ALERT
TestObjectType.CHECKBOX
TestObjectType.LABEL
// etc.
In iOS, we can't specify an accessibility id
for a UITableView
, but we can use the accessibility id
assigned to similar elements in the table view to find a specific element.
To find a specific element from a collection of similar elements on the screen, first create a
new TestObject in the Object Repository in the appropriate directory for its type. The new object should have properties for
the type
and name
for iOS (or class
and resource-id
for Android).
TIP: You should leave off the label
or text
properties in order to keep the Label generic to
represent the whole collection of elements.
Then provide an index for which element in the list you want to find (indexes start at 1):
import com.detroitlabs.katalonmobileutil.testobject.TestObjectType
int index = 3 // first element in the list is at index 1, so this gets the 3rd element
TestObject labelAtIndex = Finder.findElementAtIndex(TestObjectType.LABEL, 'Generic label element', index)
In iOS, we can't specify an accessibility id
for a UITableView
, but we can use the accessibility id
assigned to similar labels in the table view to find a specific element.
To find a specific label from a collection of similar labels on the screen, first create a
new Label TestObject in the Object Repository. The new Label should have properties for
the type
and name
for iOS (or class
and resource-id
for Android).
You should leave off the label
or text
properties in order to keep the Label generic to
represent the whole collection of labels.
Then provide the text for the specific label you want to find:
TestObject labelWithText = Finder.findLabelWithText('Generic label element', 'Label Text')
In Android, CheckBoxes
do not necessarily have a resource-id
and unlike in iOS,
the label and checkbox are not separate. To find a Checkbox on screen based on its text, in either iOS or Android:
TestObject checkbox = Finder.findCheckboxWithText('Checkbox Text')
The base components of Selenium tests are RemoteWebElements
, which are extended to WebElements
by Selenium and MobileElements
by Appium. The base components for Katalon Studio are TestObjects
. Sometimes it may be useful to convert from a MobileElement
that you found with Appium functions, to a TestObject
for use in Katalon tests so that you can use Katalon functions on it.
Add this import statement to your test file:
import org.openqa.selenium.By
import com.kms.katalon.core.mobile.keyword.internal.MobileDriverFactory
import com.kms.katalon.core.testobject.TestObject
import com.detroitlabs.katalonmobileutil.testobject.TestObjectConverter
Use an Appium function to get some MobileElements
matching an xpath:
AppiumDriver<MobileElement> driver = (AppiumDriver<MobileElement>) MobileDriverFactory.getDriver()
List<MobileElement> mobileElements = driver.findElements(By.xpath(xpathValue))
Use katalon-mobile-util's TestObjectConverter
to create TestObjects
from each of the MobileElements
:
List<TestObject> testObjects = TestObjectConverter.fromElements(mobileElements)
Or convert a single MobileElement
:
TestObject testObject = TestObjectConverter.fromElement(mobileElements.get(0))
The typical flow for interacting with a button is:
- Find the button from the TestObject Repository
- Tap the button, providing a timeout and optional
FailureHandling
behavior in the case that the button doesn't exist.
For convenience, katalon-mobile-util allows you to interact with buttons in one step:
Add this import statement to your test file:
import com.detroitlabs.katalonmobileutil.testobject.Button
In your test, tap the button using the name of the saved TestObject:
Button.tap('OK button')
By default, the timeout is 0 seconds and the test will stop with an error if the button is not found.
To control the behavior further, you can override the defaults, giving all future calls to Button.tap()
the properties:
int timeout = 3
Button.initialize(timeout, FailureHandling.OPTIONAL)
To add more control to an individual button tap, you can provide one or both of the timeout
and FailureHandling
arguments:
int timeout = 3
Button.tap('OK button', timeout)
Button.tap('OK button', FailureHandling.OPTIONAL)
Button.tap('OK button', timeout, FailureHandling.OPTIONAL)
iOS and Android text fields are represented differently within the structure of a screen. iOS text fields are often directly accessible as XCUIElementTypeTextField
with an accessibility id
or (name
as it is referred to in Katalon Studio TestObjects
). We can interact with these fields using Katalon's MobileBuiltInKeywords
class:
MobileBuiltInKeywords.setText(textFieldObject, 'Text to set', timeout)
Android text fields are sometimes auto-wrapped in an android.widget.RelativeLayout
, where the RelativeLayout
gets the resource-id
reference, not the text field itself. Katalon's MobileBuiltInKeywords.setText()
doesn't work with RelativeLayout
objects.
katalon-mobile-util provides a wrapper to interact with text fields in both iOS or Android; the implementation is determined by the test device platform.
Add this import statement to your test file:
import com.detroitlabs.katalonmobileutil.testobject.TextField
If you are testing on an Android device, and the text fields have a clear button, be sure to create a TestObject
for that button:
TestObject clearButton = Finder.findButton('Clear text field button')
Clear and set the text field:
int timeout = 10
TestObject streetAddress = Finder.findTextField('Street address form field')
TextField.clearText(streetAddress, timeout, clearButton)
TextField.typeText(streetAddress, '123 My Street', timeout)
For iOS, using a clear button is unnecessary, so it can be omitted from the call:
int timeout = 10
TestObject streetAddress = Finder.findTextField('Street address form field')
TextField.clearText(streetAddress, timeout)
TextField.typeText(streetAddress, '123 My Street', timeout)
Some TextFields don't bring up the keyboard, but allow the user to select values from a predetermined picker or drop-down list instead.
To fill in a TextField by selecting its value from a list, e.g. selecting "Michigan" from a list of states to populate the TextField:
int timeout = 10
TestObject stateField = Finder.findTextField('State form field')
TextField.selectOption(stateField, 'Michigan', timeout)
To fill in a TextField that is built from multiple values, e.g. a multi-part date, provide a value for each part of the picker. Date picker selections often get transformed when displayed in the form, so we also provide the expected value of the field when the picker selections are made. If the values don't match, the test will fail.
TextField.selectOption(expirationDateField, ['September', '12', '2018'], '9/12/2018', timeout)
To move between TextFields in a form and autofocus on the next field.
NOTE: For iOS, this will look for the "Next" or ">" button on the keyboard toolbar. Android uses the keyboard's tab key.
TextField.nextField()
To close the keyboard.
NOTE: For iOS, this will look for the "Done", "Select", or "OK" button on the keyboard toolbar. Closing the Android keyboard does not rely on these buttons.
TextField.hideKeyboard()
Katalon Studio provides a method for scrolling a list until particular text is visible on the screen. Unfortunately, this function has proven problematic and results in erratic scrolling.
katalon-mobile-util provides a wrapper to scroll lists using the Appium TouchAction.
Add these imports statement to your test file:
import com.detroitlabs.katalonmobileutil.touch.Scroll
import com.detroitlabs.katalonmobileutil.touch.Scroll.ScrollFactor
Scroll list of all XCUIElementTypeStaticText
(for iOS) or *TextView
(for Android) elements until you get to the given text:
int timeout = 1
Scroll.scrollListToElementWithText('Michigan', timeout)
Where timeout
is the delay between "swipes" when scrolling.
Scroll specific list of elements with the accessibility id
(for iOS) or resource-id
(for Android) until you get to the given text. In this case, all of the elements in the collection should have the same ids, e.g. state_label
:
Scroll.scrollListToElementWithText('state_label', 'Michigan', timeout)
Once scrolling is complete, you can interact with the element, assuming you already have a TestObject named "Michigan label".
Mobile.tap(Finder.findLabel('Michigan label'), timeout)
Scroll a list of CheckBoxes
.
Scroll.scrollListToCheckboxWithText('My Option 1', timeout)
Depending on the height of the section to be scrolled, and the items within it, you may need to adjust how far each scroll action travels. For example, if the list elements are not very tall, scrolling a large distance may scroll some items off the screen so that they are not detected.
By using ScrollFactor
, you can control the degree of scrolling:
Scroll.scrollListToElementWithText('Michigan', ScrollFactor.LARGE, timeout)
The following ScrollFactors
are available:
ScrollFactor.SMALL // Scrolls roughly 25% of the scroll area on each swipe
ScrollFactor.MEDIUM // Scrolls roughly 50% of the scroll area on each swipe
ScrollFactor.LARGE // Scrolls roughly 75% of the scroll area on each swipe
ScrollFactor.XLARGE // Scrolls roughly 100% of the scroll area on each swipe
ScrollFactor
is an optional parameter (it defaults to ScrollFactor.MEDIUM
).
The default can be overridden by calling the initialize()
function:
Scroll.initialize(ScrollFactor.SMALL)
All subsequent calls to Scroll
functions during the test that don't provide a ScrollFactor will use the set ScrollFactor
.
Swiping differs from Scrolling by ignoring the elements of the screen and performing an action as if the user was swiping across the screen in a given direction.
Available directions to swipe are LEFT_TO_RIGHT
, RIGHT_TO_LEFT
, TOP_TO_BOTTOM
, and BOTTOM_TO_TOP
.
(The first position is where the swipe action starts and the second position is where it will end, e.g. BOTTOM_TO_TOP
will start a press at the bottom of the screen and release at the top of the screen, effectively scrolling down through a list).
Add this import statement to your test file:
import com.detroitlabs.katalonmobileutil.touch.Swipe
import com.detroitlabs.katalonmobileutil.touch.Swipe.SwipeDirection
Swipe in a specific direction:
Swipe.swipe(SwipeDirection.BOTTOM_TO_TOP)
Katalon Studio logs output from println()
to the console, but it is often difficult to parse out your particular statement from the logs already being created by Appium and Katalon, with INFO and DEBUG being the only log level options.
Katalon Studio also provides a way to write to the Log Viewer tab using KeywordLogger
. However, the logged statements are mixed in with the test results in this case.
katalon-mobile-util provides a cleaner way to log your own events to a file of your choosing.
The Logger
provides multiple levels of logging so you can control what goes into your file:
OFF
- turn logging offDEBUG
- diagnostics for troubleshootingINFO
- general information about how the system is configured or major process stepsWARN
- situations that can be automatically recoveredERROR
- problems that affect the current operation, but not the overall systemFATAL
- events that would force catastrophic system failures
Add these import statements to your test file:
import com.detroitlabs.katalonmobileutil.logging.Logger as Logger
import com.detroitlabs.katalonmobileutil.logging.Logger.LogLevel as LogLevel
Initialize the Logger
with the LogLevel
for which you want to see output in your file. Setting a particular log level will show all events at that level or higher.
Logger.initialize("/tmp/katalon.log", LogLevel.DEBUG)
Logger.debug("This DEBUG message will be logged to the file.")
Logger.info("This INFO message will be logged to the file.")
Logger.warn("This WARN message will be logged to the file.")
Logger.error("This ERROR message will be logged to the file.")
Logger.fatal("This FATAL message will be logged to the file.")
Logger.initialize("/tmp/katalon.log", LogLevel.INFO)
Logger.debug("This DEBUG message will NOT be logged to the file.")
Logger.initialize("/tmp/katalon.log", LogLevel.OFF)
Logger.debug("This DEBUG message will NOT be logged to the file.")
Logger.info("This INFO message will NOT be logged to the file.")
Logger.warn("This WARN message will NOT be logged to the file.")
Logger.error("This ERROR message will NOT be logged to the file.")
Logger.fatal("This FATAL message will NOT be logged to the file.")
Chris Trevarthen c/o Detroit Labs
katalon-mobile-util is available under the Apache License, Version 2.0. See the LICENSE file for more info.