34
Introduce Appium @testotips.io

20150319 testotipsio

Embed Size (px)

Citation preview

Page 1: 20150319 testotipsio

Introduce [email protected]

Page 2: 20150319 testotipsio

Twitter id: @Kazu_cocoa

Page 3: 20150319 testotipsio

Appium?

Page 4: 20150319 testotipsio

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

from: http://appium.io/

Page 5: 20150319 testotipsio

from: http://www.3pillarglobal.com/insights/appium-a-cross-browser-mobile-automation-tool

Page 6: 20150319 testotipsio

Integration TestUnit Test

Flow of automated test

GUI Test Feature Test (E2E Test)

Page 7: 20150319 testotipsio

GUI / Feature testing

Integration TestUnit TestGUI Test

Feature Test (E2E Test)

Page 8: 20150319 testotipsio

$ npm install appium ($ npm install -g appium)

Page 9: 20150319 testotipsio

node_module/.bin

appium.js appium-doctor.js authorize_ios.js

Page 10: 20150319 testotipsio

$ $(npm bin)appium

Page 11: 20150319 testotipsio

node_modules/appium/bin/appium.jsvar appium = require('../lib/server/main.js');!var startRepl = function () { var help = function () { console.log("\nWelcome to the Appium CLI".cyan); console.log(" - Access the appium object via the object: 'appium'".grey); console.log(" - appium.run is the function to start the server".grey); console.log(" - args is the default params data structure".grey); console.log(" - set args.app then run appium.run(args);\n".grey); return 'Thanks for asking'; };! help();! var r = repl.start('(appium): '); r.context.appium = appium; r.context.parser = parser(); r.context.help = help; r.context.args = { app: '/path/to/test/app' , udid: null , address: '127.0.0.1' , port: 4723 };! var connections = 0; var server = net.createServer(function (socket) { connections += 1; socket.setTimeout(5 * 60 * 1000, function () { socket.destroy(); }); repl.start("(appium): ", socket); }).listen(process.platform === "win32" ? "\\\\.\\pipe\\node-repl-sock-" + process.pid : "/tmp/node-repl-sock-" + process.pid);! r.on('exit', function () { server.close(); process.exit(); });};

Page 12: 20150319 testotipsio

node_modules/appium/lib/server/main.jsvar http = require('http') , express = require('express') , favicon = require('serve-favicon') , bodyParser = require('body-parser') , methodOverride = require('method-override') , morgan = require('morgan') // logger , routing = require('./routing.js') , path = require('path') , appium = require('../appium.js') , parserWrap = require('./middleware').parserWrap , appiumVer = require('../../package.json').version , appiumRev = null , async = require('async') , helpers = require('./helpers.js') , logFinalWarning = require('../helpers.js').logFinalDeprecationWarning , getConfig = require('../helpers.js').getAppiumConfig , allowCrossDomain = helpers.allowCrossDomain , catchAllHandler = helpers.catchAllHandler , checkArgs = helpers.checkArgs , configureServer = helpers.configureServer , startListening = helpers.startListening , conditionallyPreLaunch = helpers.conditionallyPreLaunch , prepareTmpDir = helpers.prepareTmpDir , requestStartLoggingFormat = require('./helpers.js').requestStartLoggingFormat , requestEndLoggingFormat = require('./helpers.js').requestEndLoggingFormat , domainMiddleware = require('./helpers.js').domainMiddleware;

Page 13: 20150319 testotipsio

var loggerjs = require('./server/logger.js') , logger = loggerjs.get('appium') , UUID = require('uuid-js') , _ = require('underscore') , Capabilities = require('./server/capabilities') , IOS = require('./devices/ios/ios.js') , Safari = require('./devices/ios/safari.js') , Android = require('./devices/android/android.js') , Selendroid = require('./devices/android/selendroid.js') , Chrome = require('./devices/android/chrome.js') , FirefoxOs = require('./devices/firefoxos/firefoxos.js') , jwpResponse = require('./devices/common.js').jwpResponse , status = require("./server/status.js");!var DT_IOS = "ios" , DT_SAFARI = "safari" , DT_ANDROID = "android" , DT_CHROME = "chrome" , DT_SELENDROID = "selendroid" , DT_FIREFOX_OS = "firefoxos";

node_modules/appium/lib/appium.js

Page 14: 20150319 testotipsio

!node_modules/appium/lib/server/capabilities.js

var _ = require('underscore') , logger = require('./logger.js').get('appium') , warnDeprecated = require('../helpers.js').logDeprecationWarning;!var capsConversion = {...};!var okObjects = [...];!var requiredCaps = [...];!var strictRequiredCaps = [...];!var generalCaps = strictRequiredCaps.concat([...]);!var androidCaps = [...];!var iosCaps = [...];

Page 15: 20150319 testotipsio

Appium meets

iOS

Page 16: 20150319 testotipsio

CapabilitiesIOS_CAPS_81 = { automationName: 'Appium', platformName: :ios, platformVersion: '8.1', deviceName: 'iPhone 6 Plus', app: IOS_APP_PATH, bundleId: APP_BUNDLE_ID, ! calendarFormat: 'gregorian', ! sendKeyStrategy: 'grouped' ! localizableStringsDir: 'ja.lproj', language: 'ja', locale: 'ja_JP', ! screenshotWaitTimeout: 20 }

Page 17: 20150319 testotipsio

create new plist before start app appium/lib/devices/ios/ios.js

IOS.prototype.setDeviceTypeInInfoPlist = function (cb) { var plist = path.resolve(this.args.app, "Info.plist"); var dString = this.getDeviceString(); var isiPhone = dString.toLowerCase().indexOf("ipad") === -1; var deviceTypeCode = isiPhone ? 1 : 2; parsePlistFile(plist, function (err, obj) { if (err) { logger.error("Could not set the device type in Info.plist"); return cb(err, null); } else { var newPlist; obj.UIDeviceFamily = [deviceTypeCode]; if (binaryPlist) { newPlist = bplistCreate(obj); } else { newPlist = xmlplist.build(obj); } fs.writeFile(plist, newPlist, function (err) { if (err) { logger.error("Could not save new Info.plist"); cb(err); } else { logger.debug("Wrote new app Info.plist with device type"); cb(); } }.bind(this)); } }.bind(this));};

Page 18: 20150319 testotipsio

Appium meets

Android

Page 19: 20150319 testotipsio

CapabilitiesANDROID_CAPS = { automationName: 'Appium', platformName: :android, platformVersion: '4.2', deviceName: ‘Android’, app: ANDROID_APP_PATH, !

unicodeKeyboard: true, !

fullReset: true, screenshotWaitTimeout: 20 }

Page 20: 20150319 testotipsio

appium/lib/devices/android/android.jsAndroid.prototype.pushAppium = function (cb) { logger.debug("Pushing appium bootstrap to device..."); var binPath = path.resolve(__dirname, "..", "..", "..", "build", "android_bootstrap", "AppiumBootstrap.jar"); fs.stat(binPath, function (err) { if (err) { cb(new Error("Could not find AppiumBootstrap.jar; please run " + "'grunt buildAndroidBootstrap'")); } else { this.adb.push(binPath, this.remoteTempPath(), cb); } }.bind(this));};!Android.prototype.startApp = function (args, cb) { if (args.androidCoverage) { this.adb.androidCoverage(args.androidCoverage, args.appWaitPackage, args.appWaitActivity, cb); } else { this.adb.startApp({ pkg: args.appPackage, activity: args.appActivity, action: args.intentAction, category: args.intentCategory, flags: args.intentFlags, waitPkg: args.appWaitPackage, waitActivity: args.appWaitActivity, optionalIntentArguments: args.optionalIntentArguments, stopApp: args.stopAppOnReset }, cb); }};

Page 21: 20150319 testotipsio

appium/lib/devices/android/bootstrap/

Page 22: 20150319 testotipsio
Page 23: 20150319 testotipsio

例えば、UiDeviceのpressBack()を使う

Page 24: 20150319 testotipsio

Common Selectors

Page 25: 20150319 testotipsio

exports.checkValidLocStrat = function (strat, includeWeb, cb) { if (typeof includeWeb === "undefined") { includeWeb = false; } var validStrats = [ 'xpath', 'id', 'name', 'class name' ]; var nativeStrats = [ '-ios uiautomation', 'accessibility id', '-android uiautomator' ]; var webStrats = [ 'link text', 'css selector', 'tag name', 'partial link text' ]; var nativeDeprecations = {}; var webDeprecations = {}; var deprecations = {name: 'accessibility id'};!

Page 26: 20150319 testotipsio

XPathfind_element :xpath, ”なんらかのXPath"

accessibility idfind_element :accessibility_id, ”なんらかの要素"

css selectorfind_element :css_selector, ”なんらかの要素"

# Ruby Client使用時

Page 27: 20150319 testotipsio

iOS: UIAutomation

find_element :uiautomator, "new UiSelector() .className(\"android.widget.TextView\") .text(\"#{element_name}\");”

find_element :uiautomation, ”$.mainApp().alert().buttons()[‘#{text}'];"

Android: uiautomator

Page 28: 20150319 testotipsio

Run test concurrently

Page 29: 20150319 testotipsio

Appium is just serverAppium1: $appium -p 4723 & Appium2: $appium -p 4725 -bp 4726 &

Appium1

iOS App Android

Appium

appium_server = “http://127.0.0.1: 4723”

appium_server = “http://127.0.0.1: 4725”adb: 4726

Page 30: 20150319 testotipsio

Fin

Page 31: 20150319 testotipsio

One more tip

Page 32: 20150319 testotipsio

DroidDriver

Page 33: 20150319 testotipsio

DroidDriver

• Clone from Google to SauceLab(Appium)

• https://github.com/appium/droiddriver

• UiDeviceとか、UiElementとか使ってそう

• https://github.com/appium/droiddriver/tree/master/droiddriver-android_support_test

• android-support-test libraryと並行して使うユースケースもあるが、android-support-test libraryはまだ開発されて日が浅いのでoptionとしての統合に留められている

Page 34: 20150319 testotipsio

Thanks