http://www.jianshu.com/p/10164589e694
Appium Ruby Console (ARC) 顾名思义就是 Appium 控制台(命令行模式)。 https://github.com/appium/ruby_console
如果你在 敏捷实践 (1) - 我们是如何自动化 App 验收标准 中,已经安装了 ARC,这个步就可以忽略。
$ gem install appium_lib
$ gem install appium_console
在打开一个命令行窗口运行 appium 服务
$ appium
`$ git clone https://github.com/appium/sample-code.git
arc 会自动读取 appium.txt 并通知 appium 启动 appium.txt 指定的 App
app 的位置在 sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app
$ cd sample-code/sample-code/examples/ruby
# 运行 bundle 安装依赖包
$ bundle
$ arc
这个一步发生了什么? ** ARC 启动,读取 appium.txt 的配置发送给 appium **
** appium 收到请求,启动模拟器,安装 WebDriverAgent & TestApp ** 下面粘贴的日志信息可以让大家看到许多背后的细节,对理解 appium 如何工作很有帮助。
EdwarddeMBP:appium_work edwardzhou$ appium
[Appium] Welcome to Appium v1.6.3
[Appium] Appium REST http interface listener started on 0.0.0.0:4723
[HTTP] --> POST /wd/hub/session {"desiredCapabilities":{"browserName":"","version":"","platform":"ANY","javascriptEnabled":true,"cssSelectorsEnabled":true,"takesScreenshot":true,"nativeEvents":false,"rotatable":false,"newCommandTimeout":999999,"platformName":"ios","versionNumber":"10.2","deviceName":"iPhone 6","app":"/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app","automationName":"XCUITest","language":"zh","locale":"zh_CN","autoAcceptAlerts":true}}
[debug] [MJSONWP] Calling AppiumDriver.createSession() with args: [{"browserName":"","version":"","platform":"ANY","javascriptEnabled":true,"cssSelectorsEnabled":true,"takesScreenshot":true,"nativeEvents":false,"rotatable":false,"newCommandTimeout":999999,"platformName":"ios","versionNumber":"10.2","deviceName":"iPhone 6","app":"/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app","automationName":"XCUITest","language":"zh","locale":"zh_CN","autoAcceptAlerts":true},null,null,null,null]
[Appium] Creating new XCUITestDriver session
[Appium] Capabilities:
[Appium] browserName: ''
[Appium] version: ''
[Appium] platform: 'ANY'
[Appium] javascriptEnabled: true
[Appium] cssSelectorsEnabled: true
[Appium] takesScreenshot: true
[Appium] nativeEvents: false
[Appium] rotatable: false
[Appium] newCommandTimeout: 999999
[Appium] platformName: 'ios'
[Appium] versionNumber: '10.2'
[Appium] deviceName: 'iPhone 6'
[Appium] app: '/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app'
[Appium] automationName: 'XCUITest'
[Appium] language: 'zh'
[Appium] locale: 'zh_CN'
[Appium] autoAcceptAlerts: true
[debug] [XCUITest] XCUITestDriver version: 2.5.3
[BaseDriver] The following capabilities were provided, but are not recognized by appium: version, platform, javascriptEnabled, cssSelectorsEnabled, takesScreenshot, nativeEvents, rotatable, versionNumber.
[XCUITest] The capabilities 'autoAcceptAlerts' and 'autoDismissAlerts' do not work for XCUITest-based tests. Please adjust your alert handling accordingly.
[BaseDriver] Session created with session id: 136bbab8-78e5-4e5d-9cd1-0b174eeaa835
[debug] [XCUITest] Xcode version set to '8.2.1'
[debug] [XCUITest] iOS SDK Version set to '10.2'
[XCUITest] Simluator udid not provided, using desired caps to create a new simulator
[XCUITest] No platformVersion specified. Using latest version Xcode supports: '10.2' This may cause problems if a simulator does not exist for this platform version.
[iOSSim] Constructing iOS simulator for Xcode version 8.2.1 with udid 'C2E88820-BB65-4D79-923B-15C63D5FA384'
[XCUITest] Created simulator with udid 'C2E88820-BB65-4D79-923B-15C63D5FA384'.
[XCUITest] Determining device to run tests on: udid: 'C2E88820-BB65-4D79-923B-15C63D5FA384', real device: false
[BaseDriver] Using local app '/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app'
[debug] [XCUITest] Checking whether app '/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app' is actually present
[debug] [XCUITest] App is present
[debug] [iOS] Getting bundle ID from app '/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app': 'io.appium.TestApp'
[debug] [iOSLog] Starting iOS 10.2 simulator log capture
[debug] [iOSLog] System log path: /Users/edwardzhou/Library/Logs/CoreSimulator/C2E88820-BB65-4D79-923B-15C63D5FA384/system.log
[XCUITest] Setting up simulator
[debug] [iOSSim] Checking whether simulator has been run before
[debug] [iOSSim] Simulator has not been run before
[debug] [iOS] No simulator directories found.
[debug] [iOSSim] Attempting to launch and quit the simulator, to create directory structure
[debug] [iOSSim] Will launch with Safari? false
[iOSSim] Starting simulator with command: open -Fn /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app --args -CurrentDeviceUDID C2E88820-BB65-4D79-923B-15C63D5FA384
[iOSSim] Simulator log at '/Users/edwardzhou/Library/Logs/CoreSimulator/C2E88820-BB65-4D79-923B-15C63D5FA384/system.log'
[iOSSim] Tailing simulator logs until we encounter the string "com.apple.springboard"
[iOSSim] We will time out after 60000ms
[debug] [iOSSim] Waiting an extra 10000ms for the simulator to really finish booting
[debug] [iOSSim] Done waiting extra time for simulator
[iOSSim] Simulator booted in 11143ms
[debug] [iOSSim] Checking whether simulator has been run before
[debug] [iOSSim] Simulator has been run before
[debug] [iOSSim] Killing all iOS Simulators
[debug] [iOS] Setting locale information
[debug] [iOSSim] New language: zh
[debug] [iOSSim] New locale: zh_CN
[debug] [iOSSim] Writing new locale plist data
[debug] [iOS] Locale was updated. Stopping simulator.
[debug] [iOS] Killing the simulator
[debug] [iOSSim] Killing all iOS Simulators
[debug] [iOS] No iOS / app preferences to set
[XCUITest] Simulator with udid 'C2E88820-BB65-4D79-923B-15C63D5FA384' not booted. Booting up now
[debug] [iOSSim] Killing all iOS Simulators
[iOSSim] Starting simulator with command: open -Fn /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app --args -CurrentDeviceUDID C2E88820-BB65-4D79-923B-15C63D5FA384
[iOSSim] Simulator log at '/Users/edwardzhou/Library/Logs/CoreSimulator/C2E88820-BB65-4D79-923B-15C63D5FA384/system.log'
[iOSSim] Tailing simulator logs until we encounter the string "com.apple.springboard"
[iOSSim] We will time out after 60000ms
[debug] [iOSSim] Waiting an extra 10000ms for the simulator to really finish booting
[debug] [iOSSim] Done waiting extra time for simulator
[iOSSim] Simulator booted in 22072ms
[debug] [XCUITest] Installing app '/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app' on device
[XCUITest] Using WDA path: '/Users/edwardzhou/servers/node-v7.4.0/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent'
[XCUITest] Using WDA agent: '/Users/edwardzhou/servers/node-v7.4.0/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/WebDriverAgent.xcodeproj'
[XCUITest] Launching WebDriverAgent on the device
[debug] [XCUITest] Carthage found: /usr/local/bin/carthage
[debug] [XCUITest] Killing hanging processes
[debug] [XCUITest] Beginning test with command 'xcodebuild build-for-testing test-without-building -project /Users/edwardzhou/servers/node-v7.4.0/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent/WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination id=C2E88820-BB65-4D79-923B-15C63D5FA384 -configuration Debug' in directory '/Users/edwardzhou/servers/node-v7.4.0/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent'
[debug] [XCUITest] Waiting up to 60000ms for WebDriverAgent to start
[debug] [XCUITest] Log file for xcodebuild test: /Users/edwardzhou/Library/Developer/Xcode/DerivedData/WebDriverAgent-ddapapcqxaoabrhjkjsyvzexgoxy/Logs/Test/98A1C0F5-03F0-4A95-9408-FE68E30628B2/Session-WebDriverAgentRunner-2017-02-26_191404-oFrhPq.log
[debug] [XCUITest] WebDriverAgent successfully started after 15361ms
[debug] [XCUITest] Sending createSession command to WDA
[debug] [JSONWP Proxy] Proxying [POST /session] to [POST http://localhost:8100/session] with body: {"desiredCapabilities":{"bundleId":"io.appium.TestApp","arguments":[],"environment":{},"shouldWaitForQuiescence":true}}
[debug] [JSONWP Proxy] Got response with status 200: {"value":{"sessionId":"AC8C1ADF-3E96-4C55-9302-73B4D7AA6B89","capabilities":{"device":"iphone","browserName":" ","sdkVersion":"10.2","CFBundleIdentifier":"local.pid.35906"}},"sessionId":"AC8C1ADF-3E96-4C55-9302-73B4D7AA6B89","status":0}
[debug] [XCUITest] Setting initial orientation to 'PORTRAIT'
[debug] [JSONWP Proxy] Proxying [POST /orientation] to [POST http://localhost:8100/session/AC8C1ADF-3E96-4C55-9302-73B4D7AA6B89/orientation] with body: {"orientation":"PORTRAIT"}
[debug] [JSONWP Proxy] Got response with status 200: {"value":{},"sessionId":"AC8C1ADF-3E96-4C55-9302-73B4D7AA6B89","status":0}
[Appium] New XCUITestDriver session created successfully, session 136bbab8-78e5-4e5d-9cd1-0b174eeaa835 added to master session list
[debug] [MJSONWP] Responding to client with driver.createSession() result: {"webStorageEnabled":false,"locationContextEnabled":false,"browserName":"","platform":"ANY","javascriptEnabled":true,"databaseEnabled":false,"takesScreenshot":true,"networkConnectionEnabled":false,"version":"","cssSelectorsEnabled":true,"nativeEvents":false,"rotatable":false,"newCommandTimeout":999999,"platformName":"ios","versionNumber":"10.2","deviceName":"iPhone 6","app":"/Users/edwardzhou/appium_work/sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app","automationName":"XCUITest","language":"zh","locale":"zh_CN","autoAcceptAlerts":true}
[HTTP] <-- POST /wd/hub/session 200 68905 ms - 645
[HTTP] --> GET /wd/hub/status {}
[debug] [MJSONWP] Calling AppiumDriver.getStatus() with args: []
[debug] [MJSONWP] Responding to client with driver.getStatus() result: {"build":{"version":"1.6.3","revision":null}}
[HTTP] <-- GET /wd/hub/status 200 11 ms - 83
[HTTP] --> POST /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/timeouts/implicit_wait {"ms":0}
[debug] [MJSONWP] Calling AppiumDriver.implicitWait() with args: [0,"136bbab8-78e5-4e5d-9cd1-0b174eeaa835"]
[debug] [XCUITest] Executing command 'implicitWait'
[debug] [BaseDriver] Set implicit wait to 0ms
[debug] [MJSONWP] Responding to client with driver.implicitWait() result: null
[HTTP] <-- POST /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/timeouts/implicit_wait 200 17 ms - 76
** iOS 模拟器就绪**
大家看到,app 首次运行弹出的 alert,就是导致 sample-code 中的测试用例无法通过的原因。
因为,所有的查询定位,都是在当前视图中处理,这个 alert 出现后就改变了当前视图。
let me show you~
在 arc 中输入 page,回车
[1] pry(main)> page
XCUIElementTypeApplication
name, label:
XCUIElementTypeOther
name, label: 3 格 Wi-Fi 信号(共 3 格)
value: SSID
XCUIElementTypeOther
name, label: 下午7:27
XCUIElementTypeOther
name, label: 电池电量:-100%
XCUIElementTypeAlert
name, label: “TestApp”可能使 iPhone 变慢
XCUIElementTypeStaticText
name, label, value: “TestApp”可能使 iPhone 变慢
XCUIElementTypeStaticText
name, label, value: 应用开发者需要更新此应用以改进其兼容性。
XCUIElementTypeButton
name, label: 好
nil
[2] pry(main)>
page 指令背后的细节 (appium log)
[HTTP] --> GET /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/context {}
[debug] [MJSONWP] Calling AppiumDriver.getCurrentContext() with args: ["136bbab8-78e5-4e5d-9cd1-0b174eeaa835"]
[debug] [XCUITest] Executing command 'getCurrentContext'
[debug] [MJSONWP] Responding to client with driver.getCurrentContext() result: "NATIVE_APP"
[HTTP] <-- GET /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/context 200 7 ms - 84
[HTTP] --> GET /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/context {}
[debug] [MJSONWP] Calling AppiumDriver.getCurrentContext() with args: ["136bbab8-78e5-4e5d-9cd1-0b174eeaa835"]
[debug] [XCUITest] Executing command 'getCurrentContext'
[debug] [MJSONWP] Responding to client with driver.getCurrentContext() result: "NATIVE_APP"
[HTTP] <-- GET /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/context 200 4 ms - 84
[HTTP] --> GET /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/source {}
[debug] [MJSONWP] Calling AppiumDriver.getPageSource() with args: ["136bbab8-78e5-4e5d-9cd1-0b174eeaa835"]
[debug] [XCUITest] Executing command 'getPageSource'
[debug] [JSONWP Proxy] Proxying [GET /source/xml] to [GET http://localhost:8100/session/AC8C1ADF-3E96-4C55-9302-73B4D7AA6B89/source/xml] with no body
[debug] [JSONWP Proxy] Got response with status 200: "{\n \"value\" : {\n \"tree\" : \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\\n<XCUIElementTypeApplication type=\\\"XCUIElementTypeApplication\\\" name=\\\" \\\" label=\\\" \\\" visible=\\\"true\\\" enabled=\\\"true\\\" x=\\\"0\\\" y=\\\"0\\\" width=\\\"375\\\" height=\\\"667\\\">\\n <XCUIElementTypeWindow type=\\\"XCUIElementTypeWindow\\\" visible=\\\"false\\\" enabled=\\\"true\\\" x=\\\"0\\\" y=\\\"0\\\" width=\\\"375\\\" height=\\\"667\\\">\\n <XCUIElementTypeOther type=\\\"XCUIElementTypeOther\\\" visible=\\\"false\\\" enabled=\\\"true\\\" x=\\\"0\\\" y=\\\"0\\\" width=\\\"375\\\" height=\\\"667\\\"\\/>\\n <XCUIElementTypeOther type=\\\"XCUIElementTypeOther\\\" visible=\\\"false\\\" enabled=\\\"true\\\" x=\\\"0\\\" y=\\\"0\\\" width=\\\"375\\\" height=\\\"667\\\"\\/>\\n <\\/XCUIElementTypeWindow>\\n <XCUIElementTypeWindow type=\\\"XCUIElementTypeWindow\\\" visible=\\\"false\\\" enabled=\\\"true\\\" x=\\\"0\\\" y=\\\"0\\\" width=\\\"375\\\" height=\\\"667\\\">\\n <XCUIElementType...
[debug] [MJSONWP] Responding to client with driver.getPageSource() result: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><AppiumAUT><XCUIElementTypeApplication type=\"XCUIElementTypeApplication\" name=\" \" label=\" \" visible=\"true\" enabled=\"true\" x=\"0\" y=\"0\" width=\"375\" height=\"667\">\n <XCUIElementTypeWindow type=\"XCUIElementTypeWindow\" visible=\"false\" enabled=\"true\" x=\"0\" y=\"0\" width=\"375\" height=\"667\">\n <XCUIElementTypeOther type=\"XCUIElementTypeOther\" visible=\"false\" enabled=\"true\" x=\"0\" y=\"0\" width=\"375\" height=\"667\"/>\n <XCUIElementTypeOther type=\"XCUIElementTypeOther\" visible=\"false\" enabled=\"true\" x=\"0\" y=\"0\" width=\"375\" height=\"667\"/>\n </XCUIElementTypeWindow>\n <XCUIElementTypeWindow type=\"XCUIElementTypeWindow\" visible=\"false\" enabled=\"true\" x=\"0\" y=\"0\" width=\"375\" height=\"667\">\n <XCUIElementTypeOther type=\"XCUIElementTypeOther\" visible=\"false\" enabled=\"true\" x=\"0\" y=\"0\" width=\"375\" height=\"667\"/>\n </XCUIElementTypeWindow>\n <XCUIElementTypeWindow type=\"XCUIElementTypeWin...
[HTTP] <-- GET /wd/hub/session/136bbab8-78e5-4e5d-9cd1-0b174eeaa835/source 200 422 ms - 14307
点击“好”,或直接在 arc 中输入 alert_accept 回车,关闭对话框
page 用于查看当前视图中有哪些元素,并以行文本形式输出
[2] pry(main)> alert_accept
{}
[3] pry(main)> page
XCUIElementTypeApplication
name, label: TestApp
XCUIElementTypeTextField
name: IntegerA
label: TextField1
XCUIElementTypeTextField
name: IntegerB
label: TextField2
XCUIElementTypeButton
name: ComputeSumButton
label: Compute Sum
XCUIElementTypeStaticText
name: Answer
label: SumLabel
value: SumLabel
XCUIElementTypeButton
name, label: show alert
XCUIElementTypeButton
name, label: contact alert
XCUIElementTypeButton
name, label: location alert
XCUIElementTypeStaticText
name, label, value: AppElem
XCUIElementTypeSlider
name, label: AppElem
value: 50%
XCUIElementTypeStaticText
name: Accessibility
XCUIElementTypeStaticText
name, label, value: AppElem
XCUIElementTypeButton
name: DisabledButton
label: disabled button
XCUIElementTypeSwitch
name, label: locationStatus
value: 0
XCUIElementTypeButton
name, label: Test Gesture
XCUIElementTypeButton
name, label: Crash
XCUIElementTypeOther
name, label: 3 of 3 Wi-Fi bars
value: SSID
XCUIElementTypeOther
name, label: 下午7:48
XCUIElementTypeOther
name, label: -100% battery power
nil
[4] pry(main)>
测试应用为 TestApp
XCUIElementTypeApplication name, label: TestApp
第一个输入框,类型为 XCUIElementTypeTextField, name (id) 为 IntegerA 文本标签 TextField1
XCUIElementTypeTextField name: IntegerA label: TextField1
....
source 作用同 Page source 返回个是 XML 的文档格式。
Ref: https://github.com/appium/ruby_lib/blob/master/docs/docs.md
Appium Ruby Lib(SeleniumDriver)真正提供的查找元素的方法是
其他的方法(find, id, xpath ...),都是语法糖。
语法糖 | 对应 find_element(s) |
---|---|
id('comp_id') | find_element(:id, 'comp_id') |
buttons | find_elements(:class, 'XCUIElementTypeButton') |
button(index) | => buttons[index] |
texts | find_elements(:class, 'XCUIElementTypeStaticText') |
text(index) | => texts[index] |
textfields | find_elements(:class, 'XCUIElementTypeTextField') |
textfield[index] | => textfields[index] |
xpath('xpath_expr') | find_element(:xpath, 'xpath_expr') |
xpaths('xpath_expr') | find_elements(:xpath, 'xpath_expr) |
finds('text') | find_elements(:xpath, "(//*)[@*[contains(translate(., \"Text\", \"text\"), \"text\")]]" |
find('text') | => finds('text')[0] |
基于性能考虑,建议尽量使用 id 查找元素,find/finds 的性能最差。
拿到一个元素 e 之后,我们就是直接访问属性或发送动作。
e.name # button, text
e.value # secure, textfield
e.type 'some text' # type text into textfield
e.clear # clear textfield
e.tag_name # calls .type (patch.rb)
e.text
e.size
e.location
e.rel_location
e.click
e.send_keys 'keys to send'
e.set_value 'value to set' # ruby_console specific
e.displayed? # true or false depending if the element is visible
e.selected? # is the tab selected?
e.attribute('checked') # is the checkbox checked?
这些属性和动作的背后,都是发送指令到 appium,可以直接在 appium 运行日志中看到每个请求的具体细节。
以后有时间再接着第一篇说说我们是如何改进测试用例,把 steps 模版化。