Introduction to Appium

About me

🇦🇷Bruno Alassia 🇨🇦

Senior Software Engineer at Visual testing team @Saucelabs

Former Emulator/Simulator team

 

Python developer, NodeJS enthusiast,

mobile development. Open source lover.

@vrunoa

vrunoa

vruno@saucelabs.com

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.

Each platform is supported by one or more "drivers", which know how to automate that particular platform.

What can be automated with appium?

Appium gives you the possibility to automate and run tests over mobile web and native application on Android and iOS, desktop applications on Windows and MacOS, youi.tv, tizen and more.

Why using Appium?

You don't have to recompile your app or modify it in any way, due to use of standard automation APIs on all platforms.

You can easily start moving from Manual testing to Automated testing without code changes.

This allow you to automate external or system apps along with yours during a test case scenario.

Why using Appium?

Appium has built-in mobile web and hybrid app support. Within the same script you can easily change between native app automation and webview automation, all using the WebDriver model that's already the standard for web automation.

Why using Appium?

You can write tests with your favorite dev tools using any WebDriver-compatible language such as Java, Objective-C, JavaScript (Node), PHP, Python, Ruby, C#, Clojure, or Perl with the Selenium WebDriver API and language-specific client libraries.

Appium client

Appium server

Device

Why I chose Appium?

Manual testing

Automated testing

...

...

...

...

...

...

...and of course... because it was open source 

Let's see it running

1. the client talks to Appium server to start a session on an device

2. Appium instanciate the driver that will manage the communication with the device

3. The driver prepares the device, make sure it can talk to the device and once it's ready tells the appium server

4. Appium server replies back to the client that the session is ready and that it can receive the commands

And then we go thru the same process for each command we have in our test

Let's take a quick look at a Selenium test

import unittest
import sys
from time import sleep
from selenium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4444/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "chrome"
        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)

def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

How hard would it to run the same test on an android device?

import unittest
import sys
from time import sleep
from selenium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4444/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "Mac OS"
        capabilities["platformVersion"] = "10.14.5"
        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)

def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()
import unittest
import sys
from time import sleep
from selenium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4444/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "Mac OS"
        capabilities["platformVersion"] = "10.14.5"
        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)

def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()
import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4444/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android Emulator"
        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()
import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4444/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android Emulator"
        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()
import unittest
import sys
from time import sleep
from appium import webdriver


class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"

        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android GoogleApi Emulator"
        capabilities["automationName'] = "uiautomator2"

        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

The client

the client is what it's going to communicate with the Appium server and drive your test

 

import unittest
import sys
from time import sleep
from appium import webdriver


class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"

        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android GoogleApi Emulator"
        capabilities["automationName'] = "uiautomator2"

        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

The capabilities

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

 

import unittest
import sys
from time import sleep
from appium import webdriver


class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"

        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android GoogleApi Emulator"

        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

Server endpoint

As we have introduced before, Appium is a server so we need to set up the endpoint where we are going to be sending commands thru the client

import unittest
import sys
from time import sleep
from appium import webdriver


class WebTest(unittest.TestCase):
    ...

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

...and the tests

On the tests we have the command calls to interact with our UI and assert that everything is working properly as expected

What can be automated with appium?

Appium gives you the possibility to automate and run tests over mobile web and native application on Android and iOS, desktop applications on Windows and MacOS, you.tv, tizen and more.

Let's write a web test for iOS

import unittest
import sys
from time import sleep
from appium import webdriver


class WebTest(unittest.TestCase):
    
def setUp(self):
        host = "http://localhost:4723/wd/hub"

        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android GoogleApi Emulator"

        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()
    
    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

Looking at our current Android test? What do we need to change to run a test on iOS?

import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android Emulator"
        self.driver = webdriver.Remote(host, capabilities)
        

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)

def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()
import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "safari"
        capabilities["platformName"] = "iOS"
        capabilities["platformVersion"] = "12.2"
        capabilities["deviceName"] = "iPhone X"
        self.driver = webdriver.Remote(host, capabilities)
        

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

What can be automated with appium?

Appium gives you the possibility to automate and run tests over mobile web and native application on Android and iOS, desktop applications on Windows and MacOS, you.tv, tizen and more.

Let's do that!

Looking at Appium documentation we can see there is an `app` capability to use when trying to start a test on a native application

import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android Emulator"
        self.driver = webdriver.Remote(host, capabilities)
import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["app"] = "/path/to/my/app"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android Emulator"
        self.driver = webdriver.Remote(host, capabilities)

Web

Native

...

class WebTest(unittest.TestCase):
    def setUp(self):
       ...

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        el = self.driver.find_element_by_accessibility_id('test-Username')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)
        el = self.driver.find_element_by_accessibility_id('test-Password')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)
        el = self.driver.find_element_by_accessibility_id('test-LOGIN')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)

    def test_login_succes_flow(self):
        el = self.driver.find_element_by_accessibility_id('test-Username')
        el.send_keys("standard_user")
        el = self.driver.find_element_by_accessibility_id('test-Password')
        el.send_keys("secret_sauce")
        el = self.driver.find_element_by_accessibility_id('test-LOGIN')
        el.click()
        sleep(2)
        self.assertEquals(self.driver.current_activity, ".MainActivity")


...
...

class WebTest(unittest.TestCase):
    def setUp(self):
        ...
        

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)

...

Web

Native

"When writing native apps testing, follow best practices. This will speed up your tests execution time and make them easy to maintain"

Same concept of `app` capability applies to iOS

...

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["app"] = path.join(CWD, 'apps', 'Android.SauceLabs.Mobile.Sample.app.1.0.0.apk')
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android GoogleApi Emulator"
        capabilities["automationName"] = "uiautomator2"
        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        el = self.driver.find_element_by_accessibility_id('test-Username')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)
        el = self.driver.find_element_by_accessibility_id('test-Password')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)
        el = self.driver.find_element_by_accessibility_id('test-LOGIN')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)

    def test_login_succes_flow(self):
        el = self.driver.find_element_by_accessibility_id('test-Username')
        el.send_keys("standard_user")
        el = self.driver.find_element_by_accessibility_id('test-Password')
        el.send_keys("secret_sauce")
        el = self.driver.find_element_by_accessibility_id('test-LOGIN')
        el.click()
        sleep(2)
        self.assertEquals(self.driver.current_activity, ".MainActivity")
...

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["app"] = path.join(CWD, 'apps', 'iOS.Simulator.SauceLabs.Mobile.Sample.app.1.0.0.app.zip')
        capabilities["platformName"] = "iOS"
        capabilities["platformVersion"] = "12.2"
        capabilities["deviceName"] = "iPhone X"
        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        el = self.driver.find_element_by_accessibility_id('test-Username')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)
        el = self.driver.find_element_by_accessibility_id('test-Password')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)
        el = self.driver.find_element_by_accessibility_id('test-LOGIN')
        self.assertIsNotNone(el)
        self.assertEquals(el.is_displayed(), True)

    def test_login_succes_flow(self):
        el = self.driver.find_element_by_accessibility_id('test-Username')
        el.send_keys("standard_user")
        el = self.driver.find_element_by_accessibility_id('test-Password')
        el.send_keys("secret_sauce")
        el = self.driver.find_element_by_accessibility_id('test-LOGIN')
        el.click()
        sleep(2)
        self.assertEquals(self.driver.current_activity, ".MainActivity")

So, I copy-pasted my Android native test and changed the capabilities to run on an iOS device.

ERROR: test_login_succes_flow (__main__.WebTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_app_ios.py", line 44, in test_login_succes_flow
    self.assertEquals(self.driver.current_activity, ".MainActivity")
  File "/Users/brunoalassia/.virtualenvs/testing/lib/python2.7/site-packages/appium/webdriver/extensions/android/activities.py", line 63, in current_activity
    return self.execute(Command.GET_CURRENT_ACTIVITY)['value']
  File "/Users/brunoalassia/.virtualenvs/testing/lib/python2.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/Users/brunoalassia/.virtualenvs/testing/lib/python2.7/site-packages/appium/webdriver/errorhandler.py", line 29, in check_response
    raise wde
WebDriverException: Message: Method has not yet been implemented

Use the Appium documentation to verify the methods used in your test works for the platform to be tested

Mobile testing at Sauce Labs

Selenium and Appium gives you the possibility to run your tests locally or in the cloud

Why using Saucelabs?

Test across different platforms

We support the latest releases on Android Emulators and iOS Simulators

Why using Saucelabs?

we support different combinations of Android emulators

iPhone and iPad simulators

Use our Real Device Cloud

we have more than 800 devices, 188 different device models you can select 

Why using Saucelabs?

Make use of concurrency to speed up your builds

Get fast feedback using Emulators & Simulators

Why using Saucelabs?

Use our real device cloud to test performance

Make sure your new code is not introducing performance regressions

...

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["app"] = "sauce-storage:my-app.apk"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "7.1"
        capabilities["deviceName"] = "Android GoogleApi Emulator"
        capabilities["automationName"] = "uiautomator2"
        capabilities["recordDeviceVitals"] = True
        self.driver = webdriver.Remote(host, capabilities)


    def tearDown(self):
        self.driver.quit()

    def test_list_loads(self):
        ...

once your test is finished you'll get a CSV with metrics on the

resources used in the device

Use this metics to keep track of your performance and compare new versions

Why using Saucelabs?

we are continuous adding tools to help you debug issues

Occasionally mobile applications crash during a test and it is hard to identify the root cause of the problem. However, users can now access crash logs generated while testing an iOS application on Sauce iOS simulators.

Why using Saucelabs?

easily tests behind a firewall or you own localhost in the cloud using SauceConnect

Tests in the cloud accesing my local laptop

Why using Saucelabs?

we care about security

 

we use fresh clean VMs on each test you ran in our platform

 

we have a security team making sure we are compliant

That's why most famous banks in the world and media services choose us

we use AES256 encryption for our assets and sauce connect data transportation

we completely destroy VMs after each tests finishes

Let's talk about the how? 

How hard is to start running your tests in the cloud?

import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "safari"
        capabilities["platformName"] = "iOS"
        capabilities["platformVersion"] = "12.2"
        capabilities["deviceName"] = "iPhone X"
        self.driver = webdriver.Remote(host, capabilities)
        

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

How many lines of code would you think we need to change in our test to start using sauce cloud?

import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "http://localhost:4723/wd/hub"
        capabilities = {}
        capabilities["browserName"] = "safari"
        capabilities["platformName"] = "iOS"
        capabilities["platformVersion"] = "12.2"
        capabilities["deviceName"] = "iPhone X"
        self.driver = webdriver.Remote(host, capabilities)
        

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

The answer is 1

import unittest
import sys
from time import sleep
from appium import webdriver

class WebTest(unittest.TestCase):
    def setUp(self):
        host = "https://%s:%s@ondemand.saucelabs.com:443/wd/hub" % (os.environ['SAUCE_USERNAME'], os.environ['SAUCE_ACCESS_KEY'])
        capabilities = {}
        capabilities["browserName"] = "safari"
        capabilities["platformName"] = "iOS"
        capabilities["platformVersion"] = "12.2"
        capabilities["deviceName"] = "iPhone X Simulator"
        self.driver = webdriver.Remote(host, capabilities)
        

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)

    def test_login_succes_flow(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elUser.send_keys("standard_user")
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        elPass.send_keys("secret_sauce")
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)
        btt.click()
        self.assertEquals(self.driver.current_url, "https://www.saucedemo.com/inventory.html")
        sleep(2)


def main():
    suite = unittest.TestLoader().loadTestsFromTestCase(WebTest)
    ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
    sys.exit(ret)

if __name__ == '__main__':
    main()

...and a half

...


class WebTest(unittest.TestCase):
    def setUp(self):
        ...
        
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "9"
        capabilities["deviceName"] = "Google Pixel"
        capabilities["appiumVersion"] = "1.13.0"
        capabilities["projectName"] = "saucedemo"
        capabilities["name"] = "Test SauceDemo login"

        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)


...
...


class WebTest(unittest.TestCase):
    def setUp(self):
        ...
        
        capabilities = {}
        capabilities["browserName"] = "chrome"
        capabilities["platformName"] = "android"
        capabilities["platformVersion"] = "8.1"
        capabilities["deviceName"] = "Android GoogleApi Emulator"
        capabilities["name"] = "Test SauceDemo login"

        self.driver = webdriver.Remote(host, capabilities)

    def tearDown(self):
        self.driver.quit()

    def test_login_elements_displayed(self):
        self.driver.get("https://www.saucedemo.com")
        elUser = self.driver.find_element_by_id('user-name')
        self.assertIsNotNone(elUser)
        elPass = self.driver.find_element_by_id('password')
        self.assertIsNotNone(elPass)
        btt = self.driver.find_element_by_class_name('btn_action')
        self.assertIsNotNone(btt)


...

Emulator

Real Device Cloud

Native test frameworks

Along with appium



 
Android Emulators
iOS Simulators
 
Android Real Devices
iOS Real Devices
 

Native test frameworks support

last ... but most most important

to conclude

Why using Saucelabs?

We're the testing experts

we're going to help you get onboard on automated testing

we're going to help you find flaky tests

we're going to help you write better tests

we're going to help you speed up your test times

with parallelization

across all our platforms

across all our testing solutions

using best practices and help you decide when and what to test

we're going to help build your pipelines

...and much much more

Thank you

@vrunoa

vrunoa

vruno@saucelabs.com

Introduction to Appium

By Bruno Alassia

Introduction to Appium

  • 48
Loading comments...

More from Bruno Alassia