Mobile testing with Appium

The workshop

Who am I ?

Bruno Alassia

Software Engineer @ SauceLabs - Emulators & Simulators Team

Fullstack developer, Nodejs enthusiast

 

  • vruno@saucelabs.com
  • @vrunoa
  • github.com/vrunoa

 

  • Browsers, Emulators, Simulators & Real devices in the cloud!
  • More than 300 combinations of browsers/OS

  • Test mobile & webapps in parallel

  • Watch videos, screenshots, logs,... of your automated tests

What is Appium ?

Appium is an open source test automation framework for use with native, hybrid and mobile web apps. 
It drives iOS, Android, Windows desktop apps and more using the WebDriver protocol

Why Appium ?

  • You can write tests with your favorite dev tools using any WebDriver-compatible language
  • Detect errors & bugs faster
  • Improve release times (Manual vs Automated)
  • Run tests in lots of devices (different os versions, architectures, screens, resolutions,...)
  • Avoid regressions (a bug that comes back)
  • Create apps demos for marketing & clients
  • Study performances

How appium works

  1. You write your tests using one of Appium client libraries
  2. Your tests calls the Webdriver API
  3. The Webdriver sends the request in form of JSon via http request to the Appium server.
  4. The Appium server, under the hood invokes vendor specific mechanisms to execute the test commands
  5. The client (devices or emulators) responds back to Appium server
  6. Appium server logs the results in console.

Demo

Appium news

  • Appium joins the JS Foundation
  • Added support for Appium to target Apple's new XCUITest framework, which includes support for iOS 10 / Xcode 8.
  • Appium team is extremely proud to release the first iteration of our integration with Microsoft's WinAppDriver, a Windows app automation technology designed from the ground up to work with Appium.
  • Appium 1.6 also ships with early beta support of an upgrade to Appium's Android support using UIAutomator 2.
  • Folks behind You.i Engine have released an Appium driver for automating apps built with their framework

Install

Android Requirements

  • Android SDK API >= 17

iOS Requirements

  • Mac OS X 10.10 or higher, 10.11.1 recommended
  • XCode >= 7.1.1 recommended (Xcode 8 ? brew install carthage)
  • Apple Developer Tools (iPhone simulator SDK, command line tools)
npm install -g appium

Before testing, make sure you have emulators|simulators available

 

  • iOS

 

      List devices      

     

 

      Create simulator

     

 

 

  • Android

 

      List devices

     

 

      Create emulator

 

 

~$ xcrun simctl list devices
xcrun simctl create "iPhone 5s" com.apple.CoreSimulator.SimDeviceType.iPhone-5s \
                     com.apple.CoreSimulator.SimRuntime.iOS-9-3
~$ emulator -list-avds
~$ android create avd -n "Nexus-5" -t "android-22" -c "1024M" --abi x86_64 -d "Nexus 5"

Running appium

appium &

Let the tests begin!

Workshop repository

git clone https://github.com/vrunoa/appium-workshop

The idea is to follow this slides and start creating tests from knowing nothing to run a test on the cloud using a tunnel.

For order of this to happen we are going to follow the exercises under this repo. Check the README inside every exercise folder. And if you have any doubts ask me or cheat by looking at the solution.

import chai from 'chai';
chai.should();

import { sleep } from 'asyncbox';

import wd from 'wd';
let driver = wd.promiseChainRemote('localhost', 4723);

let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async () => {
    await sleep(5000);
    await driver.get('https://vrunoa.github.io/appium-workshop/');
    await sleep(500);
    let title = await driver.title();
    title.should.equal("Appium Workshop");
  });
});
import wd from 'wd';
let driver = wd.promiseChainRemote('localhost', 4723);

let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

The WebDriver client

import wd from 'wd';
let driver = wd.promiseChainRemote('localhost', 4723);

let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

The Capabilities

Capabilities are the way to tell Appium which kind of session we are interested in. 

Most common capabilities

Capability Description Values
platformName Which mobile OS platform to use Android, iOS
platformVersion Mobile OS version 9.3, 4.4.3
deviceVersion The kind of mobile device or emulator to use ​iPhone Simulator, iPad Simulator,iPhone Retina 4-inch, Android Emulator...
automationName Which automation engine to use Appium(default), Selendroid, XCuiTest
browserName Name of mobile web browser to automate. Should be an empty string if automating an app instead 'Safari' for iOS and 'Chrome', 'Chromium', or 'Browser' for Android
orientation (Sim/Emu-only) start in a certain orientation LANDSCAPE or PORTRAIT
let driver = wd.promiseChainRemote('localhost', 4723);
let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async () => {
    await sleep(5000);
    await driver.get('https://vrunoa.github.io/appium-workshop/');
    await sleep(500);
    let title = await driver.title();
    title.should.equal("Appium Workshop");
  });
});

Test body

let driver = wd.promiseChainRemote('localhost', 4723);
let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async () => {
    await sleep(5000);
    await driver.get('https://vrunoa.github.io/appium-workshop/');
    await sleep(500);
    let title = await driver.title();
    title.should.equal("Appium Workshop");
  });
});

Starting a session

let driver = wd.promiseChainRemote('localhost', 4723);
let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async () => {
    await sleep(5000);
    await driver.get('https://vrunoa.github.io/appium-workshop/');
    await sleep(500);
    let title = await driver.title();
    title.should.equal("Appium Workshop");
  });
});

Opening an url

let driver = wd.promiseChainRemote('localhost', 4723);
let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async () => {
    await sleep(5000);
    await driver.get('https://vrunoa.github.io/appium-workshop/');
    await sleep(500);
    let title = await driver.title();
    title.should.equal("Appium Workshop");
  });
});

Getting the page title

let driver = wd.promiseChainRemote('localhost', 4723);
let capabilities = {
  "browserName" : "Safari",
  "deviceName" : "iPhone 5s",
  "platformName" : "iOS",
  "platformVersion" : "10.0",
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async () => {
    await sleep(5000);
    await driver.get('https://vrunoa.github.io/appium-workshop/');
    await sleep(500);
    let title = await driver.title();
    title.should.equal("Appium Workshop");
  });
});

Closing current session

Running

What if I want to run the same test on an Android emulator ?

let driver = wd.promiseChainRemote('localhost', 4723);
let capabilities = {
  "browserName" : "Browser",
  "deviceName" : "Android Emulator",
  "platformName" : "Android",
  "platformVersion" : "4.4.3",
  "avd": "Nexus-5"
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async (done) => {
    await sleep(5000);
    await driver.get('https://vrunoa.github.io/appium-workshop/');
    await sleep(500);
    let title = await driver.title();
    title.should.equal("Appium Workshop");
    done();
  });
});

Change the capabilities

Finding elements

.elementById
.elementByClassName
.elementByName
.elementByTagName

Playing with elements

.click
.flick
.sendKeys
.clear
.getAttribute
.tap
.setValue
.getValue
.text
.isDisplayed

Miscelaneous commands

.back
.getPageSource
.getPageTitle
.getWindowSize
.hideKeyboard
.getGeoLocation
.setGeoLocation
.getOrientation
.setOrientation
.setUrl

Touch gestures

let action = new TouchAction()
    action.press(100,100)
    .wait(100)
    .moveTo(150,150)
    .release()
await action.perform();

What about native apps testing ?

Use native application capabilities

let driver = wd.promiseChainRemote('localhost', 4723);
let capabilities = {
  "browserName" : "Browser",
  "deviceName" : "Android Emulator",
  "platformName" : "Android",
  "platformVersion" : "4.4.3",
  "appPackage": "com.example.android",
  "appActivity": "com.example.android.SplashActivity",
  "appWaitActivity": "com.example.android.MainActivity",
  "app": "~/Sauce/appium-workshop/examples/android/android-debug.apk"
  "avd": "Nexus-5"
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async (done) => {
    await sleep(500);
    let el = await.findElementById("button1");
    let bttText = await el.getText();
    bttText.should.equal("Button1")
    done();
  });
});

Android common capabilities

Capability Description Values
appActivity Activity name for the Android activity you want to launch from your package. .MainActivity, .Settings, .SplashActivity
appPackage Java package of the Android app you want to run com.example.android.app
appWaitActivity Activity name for the Android activity you want to wait for .MainActivity
avd Name of avd to launch Nexus-5-API-19
avdArgs Additional emulator arguments used when launching an avd -netfast, -http-proxy, -camera-front
app The absolute local path or remote http URL to an .ipa or .apk file, or a .zip containing one of these. Appium will attempt to install this app binary on the appropriate device first /abs/path/to/my.apk orhttp://myapp.com/app.ipa

iOS common capabilities

Capability Description Values
bundleId ​Bundle ID of the app under test. Useful for starting an app on a real device or for using other caps which require the bundle ID during test startup. To run a test on a real device using the bundle ID, you may omit the 'app' capability, but you must provide 'udid'. io.appium.TestApp
udid Unique device identifier of the connected physical device 1ae203187fc012g
​locationServicesEnabled (Sim-only) Force location services to be either on or off. Default is to keep current sim setting. true or false
​locationServicesAuthorized (Sim-only) Set location services to be authorized or not authorized for app via plist, so that location services alert doesn't pop up. Default is to keep current sim setting. Note that if you use this setting you MUST also use the bundleIdcapability to send in your app's bundle ID. true or false
​autoAcceptAlerts Accept all iOS alerts automatically if they pop up. This includes privacy access permission alerts  true or false
autoDismissAlerts Dismiss all iOS alerts automatically if they pop up. true or false

Changing contexts to test Hybrid apps

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async (done) => {
    await sleep(500);
    let contexts = await driver.getContexts()
    let webViewContexts = null;
    for (let i in contexts) {
        if(contexts[i].indexOf("WEBVIEW") webViewContexts = contexts[i]
    }
    webViewContexts.should.not.equal(null);
    await driver.setContexts(webViewContexts);
    let bttText = await el.getText();
    bttText.should.equal("Button1")
    done();
  });
});

Using cloud devices at SauceLabs

let driver = wd.promiseChainRemote('ondemand.saucelabs.com', 8080);
let capabilities = {
  "browserName" : "Browser",
  "deviceName" : "Android Emulator",
  "platformName" : "Android",
  "platformVersion" : "4.4",
  "appPackage": "com.example.android",
  "appActivity": "com.example.android.SplashActivity",
  "appWaitActivity": "com.example.android.MainActivity",
  "app": "sauce-storage:android-debug.apk"
};

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async (done) => {
    await sleep(500);
    let el = await.findElementById("button1");
    let bttText = await el.getText();
    bttText.should.equal("Button1")
    done();
  });
});

https://wiki.saucelabs.com/display/DOCS/Platform+Configurator#/

Testing grid

{
    "browsers": [
        { 
            "browserName": "browser", "version": "4.4", 
            "platformName": "Android", "device": "Android Emulator" 
        }, { 
            "browserName": "browser", "version": "5.0", 
            "platformName": "Android", "device": "Android Emulator"  
        }, { 
            "browserName": "browser", "version": "5.1", 
            "platformName": "Android", "device": "Android GoogleApi Emulator"  
        }, { 
            "browserName": "safari", "version": "10.0", 
            "platformName": "iOS", "device": "iPhone 6s Simulator" 
        }, { 
            "browserName": "safari", "version": "9.3", 
            "platformName": "iOS", "device": "iPhone 6s Simulator" 
        }, { 
            "browserName": "safari", "version": "9.3", 
            "platformName": "iOS", "device": "iPad Air Simulator" 
        }
    ],
    "remoteCfg": {
        "hostname": "ondemand.saucelabs.com",
        "port": 80,
        "username": "YOUR USERNAME",
        "accessKey": "YOUR-ACCESS-KEY"
    }
}

https://github.com/themouette/selenium-grid

Sauce connect, use a tunnel in the cloud

let driver = wd.promiseChainRemote('ondemand.saucelabs.com', 8080);
let capabilities = {
  "browserName" : "Browser",
  "deviceName" : "Android Emulator",
  "platformName" : "Android",
  "platformVersion" : "4.4"
 };

describe("Test Appium workshop page", _ => {
  before(async () => {
    await driver.init(capabilities);
  });
  after(async () => {
    await driver.quit();
  });
  it("Verify page title is Appium Workshop", async (done) => {
    await sleep(5000);
    await driver.get('http://localhost:2000');
    await sleep(1000);
    let el = await.findElementById("button1");
    let bttText = await el.getText();
    bttText.should.equal("Button1")
    done();
  });
});

https://wiki.saucelabs.com/display/DOCS/Setting+Up+Sauce+Connect

The future of Appium

  • Android 7.0 and ios 10.1 support
  • New Appium GUI built on electron
  • Considering Windows Phone, Applet watch support
  • Appium 2.0 roadmap

Appium, rather than bundling all the separate drivers by default, becomes a driver management interface (in addition to continuing to play the role of automation frontend server). Appium gets a set of CLI tools for users to pick and choose which drivers and versions of drivers they want to use via Appium.

 

https://github.com/appium/appium/blob/master/ROADMAP.md

Made with Slides.com