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 BaseStartScreenRecordingOptions(....));
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