Appium

Automation for Apps

...in my case, hybrid Cordova apps



Who am I ?

Tommy-Carlos Williams


   tommy@devgeeks.org

   @theRealDevgeeks


   http://blog.devgeeks.org

   http://github.com/devgeeks

What is Appium?

Appium is an open source test automation
framework for use with native and hybrid mobile apps.
It drives iOS and Android apps using the
WebDriver JSON wire protocol.

Why Automated Testing?

or: "I do unit testing already, why do I need this?"


Integration and Functional tests verify that the components
of your application work together

Unit Test - testing an individual unit, such as a method (function)
in a class, with all dependencies mocked up.


Functional Test - AKA Integration Test, testing a slice of
functionality in a system. This will test many methods and
may interact with dependencies like Databases
or Web Services.

OK, But Why Appium?



What can we automate?



Selenium JSON Wire Protocol

A description of the protocol used by WebDriver to communicate with remote instances





A REST API to communicate with your app


Testing libraries in various languages to communicate to the API

Test Using JS

WD.js – Webdriver/Selenium 2 client

Node-based selenium client - use either async or promises

/**/
browser.init({browserName:'chrome'}, function() {
  browser.get("http://admc.io/wd/test-pages/guinea-pig.html", function() {
    browser.title(function(err, title) {
      title.should.include('WD');
      browser.elementById('i am a link', function(err, el) {
        browser.clickElement(el, function() {
          /* jshint evil: true */
          browser.eval("window.location.href", function(err, href) {
            href.should.include('guinea-pig2');
            browser.quit();
          });
        });
      });
    });
  });
});

Getting Started / Boilerplate

Must set up the driver / browser object with info on the app

/**/
path = require('path');
var projectRoot = path.resolve(__dirname, '..');
//- var chai = require("chai");
//- var chaiAsPromised = require("chai-as-promised");
//- chai.use(chaiAsPromised);
//- chai.should();
var wd = require('wd')
describe('Encryptr', function() {
  this.timeout(50000);
  var browser, appURL = projectRoot + "/platforms/android/bin/Encryptr-debug.apk";
  var waitTimeout = 10000;
  before(function() {
    browser = wd.promiseChainRemote("localhost", 4723);
    return browser
      .init({
        device: 'Selendroid', // or 'iPhone Simulator'
        'app-package': 'org.devgeeks.encryptr',
        'app-activity': '.MyApp', // only for Android
        name: 'MyApp', app: '/path/to/MyApp-debug.apk', version: '',
        browserName: '', implicitWaitMs: 500
      });
  });
});

Hybrid Specific Info

Before interacting with the web view, we need to switch context

/**/
// Android
browser.window("WEBVIEW");
// iOS
browser.windowHandles()
  .then(function(handles) {
    return browser.window(handles[0]);
  });

Our First Test

Super simple. Start the app, wait for the existence of an
element, then check the text is correct

/**/
describe("Registration", function() {
  it("should have a registration link", function() {
    return browser
      .waitForElementByCss(".signupButton", waitTimeout)
      .then(function() {
        return browser.elementByCss(".signupButton");
      }).should.eventually.be.ok;
  });
  it("should have the text: 'Register for an account »'", function() {
    return browser.elementByCss(".signupButton")
      .then(function(el) {
        return el.text();
      }).should.eventually.equal("Register for an account »");
  });
});

Demo
















Something a Bit More Useful

Finding an element is all well and good, but clicking and
entering text is a lot more useful.

/**/
it("should be able to enter a new username", function() {
  return browser.waitForElementByCss("input[name=newusername]", 10000)
    .then(function(el) {
      el.sendKeys("testusername");
      return el.value;
    }).should.eventually.equal("testusername");
});
it("should be able to enter a new passphrase", function() {
  return browser.waitForElementByCss("input[name=passphrase]")
    .then(function(el) {
      el.sendKeys("testpassphrase");
      return el.value;
    }).should.eventually.equal("testpassphrase");
});
it("should be able to register", function() {
  return browser
    .waitForElementByCss(".button.signupButton", 10000)
    .then(function() {
      return browser.elementByCss(".button.signupButton");
    })
    .then(function(el) {
      return el.click();
    })
    .then(function() {
      return browser
        .waitForElementByCss(".login.dismissed", 10000);
    }).should.eventually.be.ok;
});

Demo
















Some Helpful Libraries

...to make it all just that little bit nicer

Adding to Your Workflow


/**/
mochaTest: {
  test: {
    options: {
      reporter: 'spec'
    },
    timeout: 10e3,
    src: ['functional-tests/functionalTests.js']
  }
}


Big Demo
















Thanks!


Feel free to ask me any Cordova / PhoneGap questions you may have, as long as they have nothing to do with jQuery Mobile.



Just kidding.












I'm totally not kidding...