测试 敏捷实践 (3) - Appium Ruby Console

edwardzhou · 2017年08月07日 · 最后由 dmycnm 回复于 2017年10月11日 · 7874 次阅读

http://www.jianshu.com/p/10164589e694

appium.png

1. Appium Ruby Console

Appium Ruby Console (ARC) 顾名思义就是 Appium 控制台(命令行模式)。 https://github.com/appium/ruby_console

2. 安装

如果你在 敏捷实践 (1) - 我们是如何自动化 App 验收标准 中,已经安装了 ARC,这个步就可以忽略。

$ gem install appium_lib
$ gem install appium_console

3. 启动

3.1 appium

在打开一个命令行窗口运行 appium 服务 $ appium

run_appium.png

3.2 拉取 Sample-Code

`$ git clone https://github.com/appium/sample-code.git

3.3 运行 ARC

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 **

run_arc.png

** 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 模拟器就绪**

ios_01.png

大家看到,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 回车,关闭对话框

ios_02.png

4. 测试指令

4.1 page

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

....


4.2 source

source 作用同 Page source 返回个是 XML 的文档格式。

4.3 查找元素 (find_element, find, id, xpath ...)

Ref: https://github.com/appium/ruby_lib/blob/master/docs/docs.md

Appium Ruby Lib(SeleniumDriver)真正提供的查找元素的方法是

  • find_element - 查找单个元素,如果有多个满足条件的,返回第一个。如果一个都没有,报错。
  • find_elements - 查找多个元素,并以数组形式返回。没找到,返回空数组 []。

其他的方法(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 的性能最差。


4.4 对元素执行动作

拿到一个元素 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 运行日志中看到每个请求的具体细节。

有一点需要注意的是,e.click 背后的实现是,先获取 e 的屏幕坐标,然后向该坐标发送触碰事件,并不是说能够直接控制 e 触发点击事件。因此,如果 e 被其他控件(如键盘)遮住,则触碰事件实际是其他控件接受了。这个着实坑了我们一把。


以后有时间再接着第一篇说说我们是如何改进测试用例,把 steps 模版化。

楼主有 ruby 切换成 webview 的 api 方法吗,driver.switch_to.context("WEBVIEW") 这个方法,执行后说没用这个方法

需要 登录 后方可回复, 如果你还没有账号请 注册新账号