1. Appium Ruby Console
Appium Ruby Console (ARC) 顧名思義就是Appium控制臺(tái)(命令行模式)叁鉴。
https://github.com/appium/ruby_console
2. 安裝
如果你在 敏捷實(shí)踐(1) - 我們是如何自動(dòng)化App驗(yàn)收標(biāo)準(zhǔn) 中,已經(jīng)安裝了ARC,這個(gè)步就可以忽略蚁署。
$ gem install appium_lib
$ gem install appium_console
3. 啟動(dòng)
3.1 appium
在打開(kāi)一個(gè)命令行窗口運(yùn)行 appium 服務(wù)
$ appium
3.2 拉取Sample-Code
`$ git clone https://github.com/appium/sample-code.git
3.3 運(yùn)行ARC
arc 會(huì)自動(dòng)讀取 appium.txt 并通知appium啟動(dòng)appium.txt指定的App
app的位置在sample-code/sample-code/apps/TestApp/build/release-iphonesimulator/TestApp.app
$ cd sample-code/sample-code/examples/ruby
# 運(yùn)行 bundle 安裝依賴包
$ bundle
$ arc
這個(gè)一步發(fā)生了什么恃慧?
** ARC 啟動(dòng),讀取appium.txt的配置發(fā)送給 appium **
** appium 收到請(qǐng)求,啟動(dòng)模擬器鹅髓,安裝WebDriverAgent & TestApp **
下面粘貼的日志信息可以讓大家看到許多背后的細(xì)節(jié),對(duì)理解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首次運(yùn)行彈出的alert,就是導(dǎo)致sample-code中的測(cè)試用例無(wú)法通過(guò)的原因确徙。
因?yàn)樾汛械牟樵兌ㄎ唬际窃诋?dāng)前視圖中處理鄙皇,這個(gè)alert出現(xiàn)后就改變了當(dāng)前視圖芜赌。
let me show you~
在 arc 中輸入page,回車
[1] pry(main)> page
XCUIElementTypeApplication
name, label:
XCUIElementTypeOther
name, label: 3 格 Wi-Fi 信號(hào)(共 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: 應(yīng)用開(kāi)發(fā)者需要更新此應(yīng)用以改進(jìn)其兼容性伴逸。
XCUIElementTypeButton
name, label: 好
nil
[2] pry(main)>
page 指令背后的細(xì)節(jié) (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
點(diǎn)擊 “好”缠沈,或直接在arc中輸入 alert_accept 回車,關(guān)閉對(duì)話框
4. 測(cè)試指令
4.1 page
page 用于查看當(dāng)前視圖中有哪些元素错蝴,并以行文本形式輸出
[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)>
測(cè)試應(yīng)用為T(mén)estApp
XCUIElementTypeApplication
name, label: TestApp
第一個(gè)輸入框洲愤,類型為 XCUIElementTypeTextField,
name (id) 為IntegerA
文本標(biāo)簽TextField1
XCUIElementTypeTextField
name: IntegerA
label: TextField1
....
4.2 source
source 作用同 Page
source 返回個(gè)是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 - 查找單個(gè)元素, 如果有多個(gè)滿足條件的顷锰,返回第一個(gè)柬赐。如果一個(gè)都沒(méi)有,報(bào)錯(cuò)官紫。
- find_elements - 查找多個(gè)元素肛宋, 并以數(shù)組形式返回。沒(méi)找到束世,返回空數(shù)組 []酝陈。
其他的方法(find, id, xpath ...),都是語(yǔ)法糖良狈。
語(yǔ)法糖 | 對(duì)應(yīng)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 對(duì)元素執(zhí)行動(dòng)作
拿到一個(gè)元素e之后,我們就是直接訪問(wèn)屬性或發(fā)送動(dòng)作遇西。
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?
這些屬性和動(dòng)作的背后馅精,都是發(fā)送指令到appium,可以直接在appium運(yùn)行日志中看到每個(gè)請(qǐng)求的具體細(xì)節(jié)粱檀。
有一點(diǎn)需要注意的是洲敢,e.click 背后的實(shí)現(xiàn)是,先獲取 e 的屏幕坐標(biāo)茄蚯,然后向該坐標(biāo)發(fā)送觸碰事件压彭,并不是說(shuō)能夠直接控制e觸發(fā)點(diǎn)擊事件。因此渗常,如果e被其他控件(如鍵盤(pán))遮住壮不,則觸碰事件實(shí)際是其他控件接受了。 這個(gè)著實(shí)坑了我們一把皱碘。
以后有時(shí)間再接著第一篇說(shuō)說(shuō)我們是如何改進(jìn)測(cè)試用例询一,把steps模版化。