Advanced Appium 2.0 Workshop
Appium Philosophy
- You’re testing the same app you are shipping
- Write tests in any language and framework you want
- Wrapped vendor-provided frameworks in WebDriver API (Webdriver Protocol)
- Open source
Appium 2.0 Evolution & What's New
Appium 2.0
The main differences
- Addition of Drivers & Plugin Support
- CLI Support for Drivers and Plugins
- No more JSONWP. Only W3C protocol.
- Expose common driver and functionality. Makes it easier for anyone to write new Drivers
- The core foundational modules have been moved to the 'Appium' monorepo
Appium 2.0 : Architecture
Android Architecture
JWP
Server Request
Server
Client
Appium Server
Espresso driver
Android Architecture
W3C
Server Request
Server
Client
Appium Server
Driver(espresso)
Server Request
Plugins
iOS Architecture
JWP
Server Request
WDA
Client
Appium Server
XCUI driver
iOS Architecture
W3C
Server Request
Client
Appium Server
Driver(xcuitest)
Server Request
Plugins
WDA
Appium 2.0 : Server
Server Installation
npm install -g appium
Not auto-installing any drivers or plugins
Server CLI
-ah APPIUMHOME The path to the directory where Appium will keep installed drivers, plugins,
and any other metadata necessary for its operation
--use-drivers DRIVERS A comma-separated list of installed driver names that should be active
for this server. All drivers will be active by default.
--use-plugins PLUGINS A comma-separated list of installed plugin names that should be active
for this server. To activate all plugins, you can use the single string "all" as the value (e.g. --plugins=all)
Appium Drivers & Plugins
Driver CLI
appium driver list
appium driver install xcuitest
appium driver install --source=local
--package=your-driver /path/to/your/driver
appium driver update --installed
appium driver uninstall xcuitest
Plugin CLI
appium plugin list
appium plugin install images
appium --use-plugins=images,fake
appium plugin update
appium plugin uninstall images
Appium Server : Advanced
Start Appium server programmatically
AppiumDriverLocalService service = AppiumDriverLocalService
.buildService(new AppiumServiceBuilder()
.usingPort(PORT)
.withArgument(GeneralServerFlag.BASEPATH, "/wd/hub"));
service.start();
.......
driver = new AndroidDriver(service.getUrl(), uiAutomator2Options);
Starting Appium Server Programmatically With Plugins
AppiumDriverLocalService service = AppiumDriverLocalService
.buildService(new AppiumServiceBuilder()
.usingPort(PORT)
.withArgument(GeneralServerFlag.BASEPATH, "/wd/hub")
.withArgument(GeneralServerFlag.PLUGINS, "device-farm"));
service.start();
.......
driver = new AndroidDriver(service.getUrl(), uiAutomator2Options);
Platform-wise desired capabilities
Android | iOS |
---|---|
skipUnlock | usePrebuiltWDA |
appWaitPackage | derivedDataPath |
appWaitActivity | useJSONSource |
androidInstallTimeout | iosInstallPause |
disableAndroidWatchers | maxTypingFrequency |
autoGrantPermission | mjpegServerPort |
avd | wdaLaunchTimeout |
Refer:-
https://appium.io/docs/en/writing-running-appium/caps/
https://caps.cloudgrey.io/
Android & iOS Options
UiAutomator2Options uiAutomator2Options = new UiAutomator2Options()
.setDeviceName("Android Emulator")
.setAutomationName(AutomationName.ANDROID_UIAUTOMATOR2)
.setApp(System.getProperty("user.dir") + "/VodQA.apk");
driver = new AndroidDriver(service.getUrl(), uiAutomator2Options);
wait = new WebDriverWait(driver, Duration.ofSeconds(30));
Gestures
Actions API for gestures
Horizontal Swipe using W3C Action API
x axis
y axis
(x1,y1)
(x2,y1)
Accessibility id: slider
getElementLocation - (X1,Y1)
pointerMove - (X1,Y1)
pointerDown
pointerMove - (X2,Y1)
pointerUp
pause
(0,0)
WebElement slider = driver.findElement(AppiumBy.accessibilityId("slider"));
Point source = slider.getLocation();
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence sequence = new Sequence(finger, 1);
sequence.addAction(finger.createPointerMove(ofMillis(0),
PointerInput.Origin.viewport(), source.x, source.y));
sequence.addAction(finger.createPointerDown(PointerInput.MouseButton.MIDDLE.asArg()));
sequence.addAction(new Pause(finger, ofMillis(600)));
sequence.addAction(finger.createPointerMove(ofMillis(600),
PointerInput.Origin.viewport(), source.x + 400, source.y));
sequence.addAction(finger.createPointerUp(PointerInput.MouseButton.MIDDLE.asArg()));
driver.perform(singletonList(sequence));
Horizontal Swipe
Do It Yourself
Try yourself to perform a Drag and Drop operation on Android
iOS specific gestures
Map<String, Object> args = new HashMap<>();
args.put("direction", "up");
driver.executeScript("mobile: swipe", args);
args.put("direction", "down");
driver.executeScript("mobile: swipe", args);
mobile: swipe
Map<String, Object> args = new HashMap<>();
args.put("direction", "down");
driver.executeScript("mobile: scroll", args);
args.put("direction", "up");
driver.executeScript("mobile: scroll", args);
mobile: scroll
Map<String, Object> args = new HashMap<>();
args.put("scale", 5);
driver.executeScript("mobile: pinch", args);
mobile: pinch
iOS specific gestures
Map<String, Object> args = new HashMap<>();
args.put("element", ((MobileElement) element).getId());
args.put("x", 2);
args.put("y", 2);
driver.executeScript("mobile: tap", args);
mobile: tap
// double-tap the screen at a specific point
Map<String, Object> args = new HashMap<>();
args.put("x", 100);
args.put("y", 200);
driver.executeScript("mobile: doubleTap", args);
mobile: doubleTap
// two-finger-tap an element (assume element object already
exists)
Map<String, Object> args = new HashMap<>();
args.put("element", ((MobileElement) element).getId());
driver.executeScript("mobile: twoFingerTap", args);
mobile: twoFingerTap
iOS specific gestures
// touch and hold an element
Map<String, Object> args = new HashMap<>();
args.put("element", ((MobileElement) element).getId());
args.put("duration", 1.5);
driver.executeScript("mobile: touchAndHold", args);
mobile: touchAndHold
// touch, hold, and drag based on coordinates
Map<String, Object> args = new HashMap<>();
args.put("duration", 1.5);
args.put("fromX", 100);
args.put("fromY", 100);
args.put("toX", 300);
args.put("toY", 600);
driver.executeScript("mobile: dragFromToForDuration", args);
mobile:dragFromToForDuration
Android specific gestures
https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/android/android-mobile-gestures.md
driver.executeScript("mobile: longClickGesture", ImmutableMap.of(
"elementId", ((RemoteWebElement) element).getId());
mobile: longClickGesture
driver.executeScript("mobile: doubleClickGesture", ImmutableMap.of(
"elementId", ((RemoteWebElement) element).getId());
mobile: doubleClickGesture
driver.executeScript("mobile: dragGesture", ImmutableMap.of(
"elementId", element.getId(),
"endX", 100,
"endY", 100
));
mobile: dragGesture
Do It Yourself
Try yourself to perform a Drag & Drop Gesture on Android
Build
Appium 2.0
2.0
Plugins
Update the Appium server object before it starts listening for requests
Plugins add arbitrary functionality that executes before or after the actual Appium commands.
Plugins alter the Appium server to introduce new commands and distribute them.
What is an Appium 2.0
Plugin?
The pluginE2EHarness method configures a server and driver for testing via "before all" and "after all"
-style hooks.
The driverE2ETestSuite method creates a Mocha test suite which makes HTTP requests to an in-memory server leveraging your driver.
Test Support for
Plugin & Driver
Wait Plugin
appium plugin install --source=npm appium-wait-plugin
appium --plugins=element-wait
wait = new WebDriverWait(driver, 30);
wait.until(presenceOfElementLocated(MobileBy.AccessibilityId("login")))
.click();
wait.until(presenceOfElementLocated(MobileBy.AccessibilityId("slider1")));
driver.findElementByAccessibilityId("login").click();
driver.findElementByAccessibilityId("slider1").click();
Before wait plugin
After wait plugin
Gestures Plugin
MobileElement source = (MobileElement) new WebDriverWait(driver, 30)
.until(elementToBeClickable(MobileBy.AccessibilityId("dragMe")));
MobileElement destination = (MobileElement) new WebDriverWait(driver, 30)
.until(elementToBeClickable(MobileBy.AccessibilityId("dropzone")));
driver.addCommand(HttpMethod.POST,
String.format("/session/%s/plugin/actions/dragAndDrop",
driver.getSessionId()), "dragAndDrop");
driver.execute("dragAndDrop", ImmutableMap.of(
"sourceId", source.getId(),
"destinationId", destination.getId()));
Appium Debugging & Maintenance
Android shell interactions
10057 ◯ : adb shell dumpsys battery
Current Battery Service state:
AC powered: true
USB powered: false
Wireless powered: false
Max charging current: 0
Max charging voltage: 0
Charge counter: 0
status: 2
health: 2
present: true
level: 100
scale: 100
voltage: 0
temperature: 0
technology: Li-ion
Get Battery Information
10078 ◯ : adb shell dumpsys cpuinfo
Load: 0.0 / 0.0 / 0.0
CPU usage from 312959ms to 312499ms ago (2018-10-07 10:49:37.267 to 2018-10-07 10:49:37.727):
151% 1523/system_server: 51% user + 100% kernel / faults: 7233 minor 83 major
15% 1365/surfaceflinger: 6.6% user + 8.8% kernel / faults: 1 minor
15% 1375/installd: 2.2% user + 13% kernel / faults: 113 minor
6.6% 1499/bootanimation: 4.4% user + 2.2% kernel
4.4% 1358/android.hardware.graphics.composer@2.1-service: 2.2% user + 2.2% kernel
2.2% 1288/kworker/0:1: 0% user + 2.2% kernel
2.2% 1298/logd: 0% user + 2.2% kernel / faults: 5 minor
2.2% 1299/servicemanager: 0% user + 2.2% kernel / faults: 1 minor
2.2% 1360/android.hardware.sensors@1.0-service: 0% user + 2.2% kernel / faults: 20 minor
50% TOTAL: 13% user + 26% kernel + 10% iowait
Get cpu Information
10079 ◯ : adb shell service list
List all services
Android - Espresso
- Provides automatic synchronization of test actions with UI
- Powerful Matchers available
- Access to App internals - WebViews
File appDir = new File("src/test/java/io/appium/java_client");
File app = new File(appDir, "ApiDemos-debug.apk");
DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("automationName", AutomationName.ESPRESSO);
capabilities.setCapability("deviceName", "Android Emulator");
capabilities.setCapability("app", app.getAbsolutePath());
driver = new AndroidDriver<>(new URL("http://127.0.0.1:4723/wd/hub"),
capabilities);
Android Espresso Locator Strategy
Data Matcher
String selector = new Json().toJson(ImmutableMap.of(
"name", "hasEntry",
"args", ImmutableList.of("title", "Sweep")));
driver.findElement(MobileBy.androidDataMatcher(selector)).click();
http://appium.io/docs/en/writing-running-appium/android/espresso-datamatcher-selector/
iOS Locator Strategy
http://appium.io/docs/en/writing-running-appium/ios/ios-predicate/
https://github.com/facebook/WebDriverAgent/wiki/Predicate-Queries-Construction-Rules
Predicates
driver.findElementByIosNsPredicate("name like 'Answer'")
class chain
driver.findElementByIosClassChain("**/XCUIElementTypeTable
[`name == "table"`]/XCUIElementTypeCell[`visible == 1`]")
https://github.com/facebook/WebDriverAgent/wiki/Class-Chain-Queries-Construction-Rules
https://github.com/facebook/WebDriverAgent/wiki/How-To-Achieve-The-Best-Lookup-Performance#select-the-most-effective-lookup-strategy
Accessibility Identifier
driver.findElementByAccessibilityId("ComputeSumButton")
Appium Advanced usecases
Parallel Test Execution
Android | iOS |
---|---|
UDID - Device ID | UDID - Real device/SimID |
chromedriverport - incase of webviews or chrome | wdalocalport - unique port no as WDA defaults to 8100 |
systemPort - If you are using UIA2 mode, set a different system port for each Appium instance set with systemPort capability since sometimes there can be a port conflict if different ports aren't used, such as in this issue.(https://github.com/appium/appium/issues/7745) |
deviceName - Simulator Name (sim only) platformVersion -Simulator OS version (sim only) |
Refer: http://appium.io/docs/en/advanced-concepts/parallel-tests/
Device Farm Plugin
appium plugin install --source=npm appium-device-farm
appium --use-plugins=device-farm
- Automatically detects connected Android, iOS Simulators, and Real devices before session creation and maintains it in the device pool.
- Dynamically allocates a free device from the device pool while creating a driver session.
- Updates the device pool with a new device during test execution.
- Queues the test cases when no free device is available for session creation.
- Allocates random ports for parallel execution.
Automating Hybrid Apps - Tips & Tricks
Set<String> contextNames = driver.getContextHandles();
for (String contextName : contextNames) {
System.out.println(contextName); //prints out something like NATIVE_APP \n WEBVIEW_1
}
driver.context(contextNames.toArray()[1]); // set context to WEBVIEW_1
//do some web testing
String myText = driver.findElement(By.cssSelector(".green_button")).click();
driver.context("NATIVE_APP");
Automating Geo Location
driver.setLocation(new Location(49, 123, 10));
SMS Simulation (Android)
driver.sendSMS("555-555-5555", "Your message here!");
Video Recording API
driver.startRecordingScreen();
driver.startRecordingScreen(new AndroidStartScreenRecordingOptions(...));
driver.stopRecordingScreen();
driver.stopRecordingScreen(new BaseStopScreenRecordingOptions(....));
Seeding Photos on Android & iOS Simulators
driver.pushFile("/mnt/sdcard/Pictures/myPhoto.jpg",
"/path/to/photo/locally/myPhoto.jpg");
Appium 2.0 for users
What you should do next?
- Read the documentation, understand the drivers and plugins way of working.
- Update client frameworks to the latest
- Validate whether they use W3C protocol
- The appium:automationName capability will be required
- Test your current scripts with 'appium@next' to make sure they are ready for an upgrade to Appium 2.0
- Drivers and plugins must be installed
Thank you
Advanced Appium 2.0 Workshop
By Srinivasan Sekar
Advanced Appium 2.0 Workshop
- 1,666