整理了網上的資源,希望對大家有幫助吧,不喜勿噴
一、Appium簡介
Appium是一個移動端的自動化框架,是跨平臺的。可用於IOS和Android以及firefox的操作系統。
• 原生應用是指用android或ios的sdk編寫的應用;
• 移動網頁web應用是指網頁應用,類似於ios中safari應用或者Chrome應用或者類瀏覽器的應用;
• 混合應用是指一種包裹webview的應用。
1.1 Appium架構原理
Appium是在手機操作系統自帶的測試框架基礎上實現的,Android4.2版本以上使用的是UIAutomator,Android4.2及以下使用的是基於Android Instrumentation框架實現的自動化測試工具;iOS是基於iOS自帶的UI自動化工具UIAutomation實現的。
Appium由客戶端和服務器組成,客戶端與服務器通過JSON Wire Protocol進行通信。下圖簡單的介紹了各部分。
Appium Server:
Appium server使用node.js寫的http服務器,遵守REST風格。主要作用是接受從Appium客戶端發起的連接,監聽客戶端發送來的命令,將命令發送給Bootstrap.jar(或Bootstrap.js)執行,並將執行結果通過HTTP應答反饋給Appium客戶端。
Bootstrap.jar:
在Android手機上運行的一個應用程序,它在手機上扮演TCP服務器的角色。當Appium需要運行命令時,Appium服務器會與Bootstrap.jar建立TCP通信,Bootstrap.jar負責運行測試。
Appium Clients:
是一個擴展WebDriver 協議的庫,負責與Appium服務端建立連接,並將腳本的指令發動到服務端。支持多種語言。
Session:
Appium的客戶端於服務端之間進行通信都必須在一個Session的上下文中進行。客戶端在發起通信的時候,會首先發動一個叫“Desired Capabilities”的JSON對象給服務器。服務器收到該數據後,會創建一個Session並將Session ID返回給客戶端。客戶端可以用此ID發送命令。
Desired Capabilities:
是一組設置的鍵值對的集合,主要用於通知Appium服務器建立需要的Session,其中一些設置可以在Appium運行過程中改變Appium服務器的運行行爲。
1.2 Appium優缺點
優點:
- 支持多種應用程序的測試
- 支持使用多種語言來編寫測試腳本
- 被測試的應用程序不需要特殊的編譯
- Appium支持應用之間跳轉的測試
缺點:
- 由於服務端運行在電腦上,該工具必須連接電腦纔可以運行
- 只能用於UI的自動化測試,在很多情況下的測試驗證只能通過驗證界面來進行
1.3 WebDriver
Appium採用底層驅動商提供統一的WebDriver API,它和Selenium有着千絲萬縷的聯繫,很多方法的使用都很相似,可以參考筆者之前寫過的Selenium文章。
Selenium自動化測試-入門
Selenium自動化測試-unittest單元測試框架使用
二、Appium環境搭建
2.1 安裝Appium運行環境
- Android運行環境
安裝Android SDK後,並將其加入到系統環境變量中。 - 安裝Python
- 安裝Node.js
是爲了用命令行的方式啓動Appium。 - 安裝Appium服務器
可以從此網站下載安裝http://appium.io/
2.2 Appium服務器啓動
打開Appium軟件後,點擊右上角的三角形,可以打開啓動服務器,如下所示:
如果輸出類似如下信息,沒有錯誤提示,就表示啓動成功了。
> Launching Appium server with command: C:\tools\Appium\node.exe lib\server\main.js --address 127.0.0.1 --port 4723 --platform-name Android --platform-version 23 --automation-name Appium --device-name "8c28b78c" --log-no-color
> info: Welcome to Appium v1.4.16 (REV ae6877eff263066b26328d457bd285c0cc62430d)
> info: Appium REST http interface listener started on 127.0.0.1:4723
> info: [debug] Non-default server args: {"address":"127.0.0.1","logNoColors":true,"deviceName":"8c28b78c","platformName":"Android","platformVersion":"23","automationName":"Appium"}
> info: Console LogLevel: debug
- 1
- 2
- 3
- 4
- 5
- 6
啓動之後,可以在瀏覽器裏面訪問http://localhost:4723/看看是否有反應,如果正常啓動的話,肯定是有反應的。我們需要在設置中改變一些設置,也可以將界面中的log信息導出到文件中,便於我們處理。
三、編寫腳本前的準備
3.1 查看頁面元素
Native APP:
我們可以使用Android SDK安裝目錄下的uiautomatorviewer來查看APP的頁面元素,~\sdk\tools\uiautomatorviewer.bat
。
也可以使用Appium inspector來查看,但是沒有uiautomatorviewer那麼好用。
含有webview的APP:
可以通過Chrome的DevTools來獲取,在Chrome中輸入chrome://inspect/#devices
後,如果有連接上的設備,可以點擊inspect進入查看頁面。
不過,有時通過這種方法是無法獲取到頁面的,原因可能是被測程序的WebView沒有開debug模式等。這時我們可以獲取當前頁面的URL然後通過Chrome或Firefox等來訪問並且查看元素。
3.2 相關文檔
這個網站上說明了Appium的方方面面,如設計理念、各個平臺的安裝、腳本編寫等等。http://appium.io/slate/cn/master/?python#about-appium
通過appium在GitHub上的介紹我們可以獲取編寫腳本的一些方法,這裏給出的是Python語言的鏈接。https://github.com/appium/python-client
3.3 簡單示例
用Python寫Appium的腳本時,只需以下幾步即可以構造一個基本的用例,如下代碼片斷所示:
#構造Desired Capabilities
desired_caps = {}
desired_caps['platformName'] = 'Android'
desired_caps['platformVersion'] = '6.0.1'
desired_caps['deviceName'] = '8c28b78c'
desired_caps['appPackage'] = 'com.ss.android.article.news'
desired_caps['appActivity'] = '.activity.SplashActivity'
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
#1.獲取元素
videoBtn = driver.find_element_by_name("視頻")
#2.操作元素
videoBtn.click()
#3.結果驗證
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
我們首先需要構造一個Desired Capabilities,設置一些參數,用它來連接到APP中,然後就是進行UI自動化操作的標準3步了。
- 獲取頁面控件
- 操作控件
- 控件信息驗證
對這三步很熟悉了之後,我們再在這個基礎上做一些封裝,使得腳本更加健壯,可維護性更高。接下來按照以上幾步來一步步做吧。
四、Desired Capabilities說明
Desired Capabilities就是一組設置,這些設置可以讓測試腳本控制Appium的運行行爲。下面對這些設置做一個簡單的說明。從其官方網站我們可以得到全面的信息,網址爲:http://appium.io/slate/en/master/?java#appium-server-capabilities
4.1 與Appium服務器相關的
Capability | 是否爲必填項 | 描述 | 值 |
---|---|---|---|
automationName | 否 | Appium使用的測試引擎 | Appium(默認) |
platformName | 是 | 被測設備的系統平臺 | iOS,Android,Firefox OS,null(默認) |
platformVersion | 否 | 手機系統版本 | 如6.6.1,null(默認) |
deviceName | 否 | 測試設備類型(測試Android時被忽略) | null(默認) |
app | 否 | 指向APP安裝文件,Android中如果設置了appActivity和appPackage,則此會被忽略 | null(默認) |
browserName | 否 | 手機網頁測試時瀏覽器的名稱 | 設置爲Safari在測iOS和Chrome時,設置爲Browser在測Android時 |
newCommandTimeout | 否 | Appium服務器等待Appium客戶端發送新消息的時間,單位爲s | 60s(默認) |
language | 否 | (僅模擬器使用)設置模擬器的語言 | null(默認) |
locale | 否 | (僅模擬器使用)設置模擬器的使用國家 | null(默認) |
udid | 否 | (僅真機使用)測試設備的ID | 在多臺設備與同一臺電腦連接時必須指定 |
orientation | 否 | (僅模擬器使用)屏幕方向 | LANDSCAPE,PORTRAIT,null(默認) |
autoWebview | 否 | 直接切換到WebView上下文 | false(默認),true |
noReset | 否 | 在一個Session開始前不重置被測程序的狀態 | false(默認),true |
fullReset | 否 | 完全重置(Android通過卸載程序的方式),Session完成後會卸載程序 | false(默認),true |
~
4.2 僅對Android測試有效的設置
Capability | 是否爲必填項 | 描述 | 值 |
---|---|---|---|
appActivity | 是 | 被測APP啓動的Activity名稱 | 如.MainActivity |
appPackage | 是 | 被測APP的包名 | 例如:com.example.android.myApp |
deviceReadyTimeout | 否 | 等待設備ready的超時時間 | 5s(默認) |
ignoreUnimportantViews | 否 | 會忽略一些控件,加快運行 | false(默認),true |
disableAndroidWatchers | 否 | 只針對基於UIAutomator的測試有效,不會監控ANR和Crash,這將較少CPU消耗 | false(默認),true |
unicodeKeyboard | 否 | 是否支持Unicode的鍵盤,如果輸入中文,設置爲是 | false(默認),true |
resetKeyboard | 否 | 測試結束後是否恢復鍵盤,爲正常的手機鍵盤 | false(默認),true |
androidScreenshotPath | 否 | 截圖存放的目錄 | /data/local/tmp(默認) |
… | … | … | … |
~
關於Android測試的Capability非常的多,以上只是其中常用的一部分。還有iOS相關的沒有在這裏敘述了,有興趣的可以訪問前面給出的官網地址去查看。
五、獲取控件
5.1 Native APP
API | 方法描述 |
---|---|
find_element_by_id(self,id) | 通過控件的resource id來查找控件 |
find_element_by_name(self,name) | Native APP中,name就是控件的Text |
find_element_by_class_name(self,name) | 控件的class name,網頁測試也可以用此 |
find_element_by_accessibility_id(self,id) | 控件的accessibility_id就是Content Description |
find_element_by_android_uiautomator(self,uia_string) | 根據UIAutomator的語法查找控件,是WebDriver在兼容Appium時才新加的語法 |
~
頁面中同一個ID的控件可能不止一個,最常見的就是列表項。find_element_by_id
是查找頁面中第一個ID爲指定參數的控件,find_elements_by_id
是查找頁面中所有ID爲指定參數的控件,返回一個控件列表。其他的查找方法類似。
5.2 Web&Hybrid APP
API | 方法描述 |
---|---|
find_element_by_xpath(self,xpath) | 通過控件的xpath來查找控件 |
find_element_by_css_selector(self,css_selector) | 通過控件的css_selector來查找控件 |
find_element_by_link_text(self,link_text) | 通過鏈接的text來查找控件 |
find_element_by_partiallink_text(self,link_text) | 通過鏈接的部分文本來查找控件 |
find_element_by_tag_name(self,tag_name) | 通過網頁元素的Tag查找控件 |
~
這一部分的控件查找和Selenium中的幾乎一樣,可以查看筆者之前的相關文章。
5.3 獲取控件舉例
下面代碼片段爲獲取圖中底部tab的視頻按鈕的兩種方法。第一種用到了name屬性,先找到其父控件,進一步縮小範圍,因爲頁面中可能在其他地方也有相同的name。第二種是用find_elements系列的方法,再拿到列表中的第2項,因爲下方的幾個控件id都是相同的。
self.driver.find_element_by_id("android:id/tabs").find_element_by_name("視頻").click() self.driver.find_elements_by_id("com.ss.android.article.news:id/b5e")[1].click()
- 1
對於一些找不到方法去定位的元素怎麼辦呢?首先想到的就是座標定位,爲了兼容更多的機型,可以用相對座標或者距離元素的位置來定位。更高端的方法就是可以採用圖像識別來確定要定位的元素,從而進行點擊,可以參考這篇文章。http://tmq.qq.com/2017/02/test_guide/
六、操作控件
6.1 獲取控件信息(部分)
API | 方法描述 |
---|---|
text(self) | 獲取控件顯示的文本信息 |
is_enabled(self) | 判斷是否可用了,可用返回true |
is_selected(self) | 是否被選中了,是的話返回true |
id_displayed(self) | 判斷控件是否顯示,是的話返回true |
get_attribute(self,name) | 獲取控件某項信息,如element.get_attribute(“displayed”)等同於id_displayed方法 |
parent(self) | 返回控件的父控件,返回值爲一個控件對象 |
6.2 手勢操作(部分)
主要有點擊、滑動、拖拽、放縮等常用的操作。
API | 方法描述 |
---|---|
click(self) | 點擊控件 |
clear(self) | 清楚文本框控件的文本 |
send_keys(self,*value) | 發送文本到控件中 |
tap(self,positions,duration=None) | positions是一個列表,每個列表是一個二元組最多可以同時點擊5個點;duration爲時間長短,給參數的話則是長按操作 |
swipe(self,start_x,start_y,end_x,end_y,duration=None) | 從一點滑動到另一點,時長爲毫秒 |
flick(self,start_x,start_y,end_x,end_y) | 兩點快速的滑動 |
scroll(self,origin_ele,destination_ele) | 從origin_ele控件滾動到destination_ele控件 |
drag_and_drop(self,origin_ele,destination_ele) | 把origin_ele控件拖拽到destination_ele控件的位置 |
pinch(self,element=None,percent=200,steps=50) | 在指定控件上執行縮小操作,默認縮放比例爲2,分50步完成 |
zoom(self,element=None,percent=200,steps=50) | 在指定控件上執行放大操作,默認縮放比例爲2,分50步完成 |
6.3 系統操作API(部分)
系統操作用於模擬硬件操作、設置網絡環境、獲取系統信息等,下表簡單的介紹一下常用的方法。
API | 方法描述 |
---|---|
launch_app(self) | 啓動Capability中指定的APP |
is_app_installed(self,package_name) | 判斷應用程序是否安裝 |
install_app(self,app_path) | 安裝APP,app_path指的是電腦上的apk路徑 |
close_app(self) | 如果Capability指定的APP在運行,則關閉它 |
background_app(self,seconds) | 將APP放到後臺運行一段時間 |
reset(self) | 重置當前被測APP到初始狀態 |
current_activity(self) | 獲取當前正在顯示的Activity |
start_activity(self,app_package,app_activity,**opts) | 啓動某個Activity |
pull_file(self,path) | 拉取手機上的一個文件,並以base64格式編碼返回數據,path爲手機文件路徑 |
pull_folder(self,path) | 拉取手機上的一個文件夾,打包後以base64格式編碼返回數據,path爲手機上的文件夾路徑 |
push_file(self,path,base64data) | 將一個base64編碼格式的文件從電腦推送到手機上的路徑path上 |
press_keycode(self,keycode,metastate=None) | 模擬發送一個硬件碼到手機,如返回等 |
open_notification(self) | 打開通知欄 |
network_connection(self) | 返回當前網絡連接的類型 |
set_network_connection(self,connectionType) | 設置網絡,值爲:0 未設置,1 飛行模式,2 WiFi only, 4 Data only, 6 WiFi& Data |
get_screenshot_as_file(self,filename) | 截圖並保存在電腦上,filename爲路徑及截圖名稱 |
save_screenshot(filename) | 截圖並保存在電腦上,filename爲路徑及截圖名稱 |
七、控件信息驗證
這裏我們要說的是查找並操作控件後,怎麼確定我們的操作起了作用。在實際的測試中也把它叫做檢查點,檢查點的劃分和驗證是UI自動化中的一個重點也是難點。常用的有以下方法:
- 判斷某個控件是否顯示(操作之後新出現的控件)
- 判斷某個控件是否被選中
- 判斷某個開關控件是否處於check狀態
- 判斷某個控件是否enabled
- 截圖之後和正確的進行比對
…
可以將以上判斷的方式進行封裝,便於我們在if語句和assert中使用。關於截圖對比的方式,首先要有正確的操作截圖,然後再進行對比得出結論看看是否一致,會涉及一些算法相關的知識。
八、常見問題
8.1 A new session could not be created
有一次在執行的過程中,發現輸出了以下錯誤;
selenium.common.exceptions.WebDriverException: Message: A new session could not be created. (Original error: An unknown server-side error occurred while processing the command. (Original error: unknown error: com.android.chrome is not installed on device 8c28b78c
(Driver info: chromedriver=2.18.343845 (73dd713ba7fbfb73cbb514e62641d8c96a94682a),platform=Windows NT 10.0 x86_64)))
- 1
- 2
可以發現主要的錯誤應該是com.android.chrome is not installed on device
,這個看起來應該是chrome瀏覽器的手機端,我們可以嘗試安裝它。但是,我記得手機上一直都沒有安裝過這個,最後又檢查了一下,發現原來是打開了Appium設置中的Browser,關閉此即可。
然而,除了這個原因有可能是別的原因,我們要具體分析錯誤輸出,還可以做一些事情來來降低這種情況的發生:
- 在初始化的setUp()方法中調用ADB命令強制關閉被測應用一次;
- 添加–session-override選項,命令行中或者Appium界面中;
- 在tearDown()方法中,關閉Appium的session,清理環境。
8.2 Permission to start activity denied.
在使用start_activity()方法來啓動另一個APP時,有時會遇到如下錯誤:
selenium.common.exceptions.WebDriverException: Message: Unable to launch the app: Error: Permission to start activity denied.
這時可以看到我們沒有權限打開這一個Activity,通常是因爲此Activity在清單文件裏面沒添加Android:exported=”true”,exported屬性就是設置是否允許activity被其它程序調用的。所以我們需要從啓動頁Activity打開如下所示。這在一些情況下可能會有點麻煩。
app_package='com.gotokeep.keep'
app_activity='.activity.SplashActivity'
self.driver.start_activity(app_package,app_activity)
- 1
- 2
- 3
還有一種錯誤是找不到要打開的Activity:
elenium.common.exceptions.WebDriverException: Message: Unable to launch the app: Error: Activity used to start app doesn’t exist or cannot be launched! Make sure it exists and is a launchable activity
檢查Activity是否存在,並且路徑是否填寫正確。