1. 程式人生 > >Appium自動化測試-入門

Appium自動化測試-入門

一、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-架構

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執行環境

  1. Android執行環境
    安裝Android SDK後,並將其加入到系統環境變數中。
  2. 安裝Python
  3. 安裝Node.js
    是為了用命令列的方式啟動Appium。
  4. 安裝Appium伺服器
    可以從此網站下載安裝http://appium.io/

2.2 Appium伺服器啟動

開啟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

啟動之後,可以在瀏覽器裡面訪問http://localhost:4723/看看是否有反應,如果正常啟動的話,肯定是有反應的。我們需要在設定中改變一些設定,也可以將介面中的log資訊匯出到檔案中,便於我們處理。

appium setting

appium setting2

三、編寫指令碼前的準備

3.1 檢視頁面元素

Native APP:
我們可以使用Android SDK安裝目錄下的uiautomatorviewer來檢視APP的頁面元素,~\sdk\tools\uiautomatorviewer.bat

appium uiautomator

也可以使用Appium inspector來檢視,但是沒有uiautomatorviewer那麼好用。

appium inspector

含有webview的APP:
可以通過Chrome的DevTools來獲取,在Chrome中輸入chrome://inspect/#devices後,如果有連線上的裝置,可以點選inspect進入檢視頁面。

devtools

不過,有時通過這種方法是無法獲取到頁面的,原因可能是被測程式的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.結果驗證

我們首先需要構造一個Desired Capabilities,設定一些引數,用它來連線到APP中,然後就是進行UI自動化操作的標準3步了。

  1. 獲取頁面控制元件
  2. 操作控制元件
  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 獲取控制元件舉例

appium find1

下面程式碼片段為獲取圖中底部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()

對於一些找不到方法去定位的元素怎麼辦呢?首先想到的就是座標定位,為了相容更多的機型,可以用相對座標或者距離元素的位置來定位。更高階的方法就是可以採用影象識別來確定要定位的元素,從而進行點選,可以參考這篇文章。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自動化中的一個重點也是難點。常用的有以下方法:

  1. 判斷某個控制元件是否顯示(操作之後新出現的控制元件)
  2. 判斷某個控制元件是否被選中
  3. 判斷某個開關控制元件是否處於check狀態
  4. 判斷某個控制元件是否enabled
  5. 截圖之後和正確的進行比對

可以將以上判斷的方式進行封裝,便於我們在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)))

可以發現主要的錯誤應該是com.android.chrome is not installed on device,這個看起來應該是chrome瀏覽器的手機端,我們可以嘗試安裝它。但是,我記得手機上一直都沒有安裝過這個,最後又檢查了一下,發現原來是打開了Appium設定中的Browser,關閉此即可。

appium setting

然而,除了這個原因有可能是別的原因,我們要具體分析錯誤輸出,還可以做一些事情來來降低這種情況的發生:

  1. 在初始化的setUp()方法中呼叫ADB命令強制關閉被測應用一次;
  2. 新增–session-override選項,命令列中或者Appium介面中;
  3. 在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)

還有一種錯誤是找不到要開啟的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是否存在,並且路徑是否填寫正確。