1. 程式人生 > >Appium做Android功能自動化測試

Appium做Android功能自動化測試

做了 指標 並不會 return 啟動 target socket cor enter

前言

做Android端功能自動化已有2年多的時間了,使用過的功能自動化框架有Robotium、Uiautomator、Appium。最近研究自動化case復用的方案,調研了Appium的自動化框架,並將其應用到銀行一賬通的標版中,本文詳細介紹基於Appium的Android功能自動化實戰經驗。主要包括以下幾方面內容:

  • Appium框架原理介紹
  • Appium框架常用API介紹
  • 基於Appium框架的自動化開發環境搭建
  • 自動化case開發及分層結構設計
  • 自動化測試用例書寫規範及註意事項
  • 功能自動化接入持續集成方案

Android常用功能自動化框架比較

下表給出Robotium、Uiautomator、Appium三種框架的比較。
了解各個自動化框架的特性,結合產品自身特點,選取一個合適的框架尤為重要。

技術分享圖片

Appium框架原理介紹

Appium 是一個開源、跨平臺的自動化測試工具,用於測試原生和輕量移動應用,支持iOS, Android 和 FirefoxOS 平臺。Appium 驅動蘋果的 UIAutomation 庫和 Android 的 UiAutomator 框架,使用 Selenium 的 WebDriver JSON 協議。下圖是Appium在Android端的原理架構圖

技術分享圖片


從圖中可以看出,Appium Client是對webdriver原生api的一些擴展和封裝,它配合原生的webdriver來使用,二者缺一不可。Appium Server有兩個主要功能:首先它作為一個http服務器端,接收從Appium Client發送過來的命令(可以認為是case中的具體操作)。其次,它作為bootstrap客戶端,在接收Client的命令後,通過socket方式,把這些命令發給目標android機器的bootstrap來驅動Uiautomator執行自動化操作。關於Appium的工作原理,網上資料很多,不再詳細介紹,可參考如下鏈接
http://www.2cto.com/kf/201410/347851.html
http://www.blogjava.net/qileilove/archive/2014/12/23/421659.html

Appium框架常用API介紹

Appium的API包含在Appium Client中,下表給出不同語言平臺對應的Client下載地址

技術分享圖片

下表給出Java平臺的常用API,其他API可自行百度或查看Client源碼

技術分享圖片


Appium元素定位和操作的api是分開的,這點與robotium框架不同,與Google新推出的iOS端功能自動化框架EarlGrey是類似的。關於元素定位的api需要註意:當成功定位到元素時,返回WebElement對象,但是若不能定位到元素,此api直接報錯,而不是返回NULL。這對“判斷某個控件是否存在”這樣的常用操作經常容易出錯,比如用如下代碼判斷登錄按鈕是否存在。

public boolean isLoginButtonShow(){
  WebElement wl = DriverManager.getInstance().findElementById(packagename
                    +  ":id/login_input_account");
  return wl.isDisplayed();
 }

當登錄按鈕存在時,返回true,但若登錄按鈕不存在時,以上代碼並不會返回false,而是在第2行直接崩潰。可通過try catch捕獲異常,修改如下:

public boolean isLoginButtonShow(){
    try{
    WebElement wl = DriverManager.getInstance().findElementById(packagename  
                +  ":id/login_input_account");
         return wl.isDisplayed();
  }catch (Exception e){
  return false;
   }
  }

基於Appium框架的自動化開發環境搭建

萬事開頭難,自動化開發環境的搭建會比較麻煩。以下詳細講解如何在mac os操作系統下,搭建基於Appium的自動化開發環境。
1、Android開發環境搭建(JDK/SDK/AndroidStudio)請自行百度,所需安裝包可在如下網頁中下載:
http://tools.android-studio.org
Appium for mac安裝可參考以下連接:
http://www.cnblogs.com/oscarxie/p/3894559.html
其中appium 的安裝,建議使用dmg安裝,點擊下載安裝包。“npm install -g appium”命令行安裝親測一直報錯,FQ了也報錯。
2、AndroidStudio中新建android工程AppDemo。
3、創建java module(appium):new—>new module—>java library,此java工程用來開發自動化case。
4、安裝Appium Client:創建java module (selenium),libs目錄中導入相關selenium包,點擊下載selenium壓縮包
5、appium module添加selenium依賴:file—>project structure—>modelues選中appium—>dependencies添加selenium module。
6、appium module中,新建java class,開始開發自動化case。至此AppDemo工程目錄結構如下所示:

技術分享圖片

7、啟動appium server(命令行或者圖形界面啟動),註意,啟動時Android Settings中請勾選No Reset選項,可防止每次執行case時,都重新安裝app,如下所示。

技術分享圖片


8、case執行,可通過IDE和腳本兩種方式執行case
IDE方式:case名右擊—>run
腳本方式:auto_run.sh,見下方

#!/bin/bash
#auto_run.sh
source ~/.bash_profile
cd ./AppDemo
gradle clean
gradle build
export CLASSPATH=$APPIUM_HOME/java-client-3.3.0.jar:$APPIUM_HOME/selenium-java-2.48.2-srcs.jar:$APPIUM_HOME/selenium-java-2.48.2.jar:$APPIUM_HOME/junit-4.12.jar:$APPIUM_HOME/hamcrest-core-1.3.jar:$APPIUM_HOME/libs/*  
cd ./appium
java -classpath $CLASSPATH:./build/libs/appium.jar  org.junit.runner.JUnitCore   com.incito.appiumdemo.cases.PersonalCente

其中CLASSPATH需要設置為Appium Client所在路徑,
com.incito.appiumdemo.cases.PersonalCenter為case所在類,腳本的執行方式有利於自動化接入持續集成和平臺化。
至此基於Appium的自動化開發環境搭建完成,下一節介紹如何開發自動化case及case的分層結構設計。

基於Appium的自動化case開發及case分層結構設計

首先為每條case創建一個公共的基類AppiumTestBase,內含setup和teardown兩個方法,以後每條case繼承該基類即可。代碼如下:

public class AppiumTestBase {    
  public WebDriverWait webwait;    
private AndroidDriver driver;    
@Before    
public void setUp() throws Exception {        
File classpathRoot = new File(System.getProperty("user.dir"));        
File appDir = new File(classpathRoot, "apps");        
File app = new File(appDir, Config.CURRENT_BANK);        
DesiredCapabilities capabilities = new DesiredCapabilities();        capabilities.setCapability("deviceName",Config.DEVICE_NAME);        capabilities.setCapability(CapabilityType.BROWSER_NAME, "");        capabilities.setCapability(CapabilityType.VERSION, "5.0.1");        capabilities.setCapability("platformName", "android");        
capabilities.setCapability("app", app.getAbsolutePath());        
capabilities.setCapability("udid", Config.DEVICE_NAME);//adb devices獲得的值        
driver = new AndroidDriver(new URL("http://127.0.0.1:"+Config.APPIUM_PORT+"/wd/hub"), capabilities);        
webwait = new WebDriverWait(driver,10);        
DriverManager.init(driver);        
DriverManager.initWebWait(webwait);        
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);    
}    
@After    
public void tearDown() throws Exception {        
driver.quit();    
}

setup操作包含導入待測應用apk包,設置與Appium Server連接所需參數,並初始化AndroidDriver對象。以標版登錄case為例,其需要繼承AppiumTestBase,代碼如下:

package com.incito.appiumdemo;
import org.junit.After;
import org.junit.Before;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.File;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import io.appium.java_client.android.AndroidDriver;
public class AccountLogin extends AppiumTestBase{    
/**     
* case 編號 AccountLogin_001     
* ****@throws ****Exception     
*/    
@Test    
public void testAccountLogin() throws Exception {        
Account.*getInstance*().gotoLoginPage();        
Account.*getInstance*().doLogin();    
}
/**     
* case 編號 AccountLogin_002     
* ****@throws ****Exception     
*/   
@Test    
public void testLoginout() throws Exception {        
Account.*getInstance*().gotoGestureLoginPage();        
Account.*getInstance*().doGestureLogin(Config.*CURRENT_USERNAME*);        
Personal.*getInstance*().doLogout();    
}

testAccountLogin Case中,執行了兩步操作:進入登錄頁面;執行登錄操作。這兩步操作都封裝在Account類裏面。由此引入自動化case的分層設計框架,如下圖:

技術分享圖片
Paste_Image.png

自動化開發過程中,經常遇到的一個問題是,隨著產品的不斷更新叠代,APP的UI會不斷發生變化,自動化如何去應對這樣的變化,如何降低其維護代價。case分層設計主要是基於自動化可維護性的考慮,可維護性是功能自動化最重要的評價指標之一,其直接決定了自動化是否能開展下去。試想case數量達到一定程度時,若沒有采用封裝、分層的設計思路,極有可能出現“牽一發而動全身”的問題。下圖是標版自動化case的分層目錄圖。

技術分享圖片 下面以登錄case為例,詳細了解其分層調用關系。登錄case在cases層的AccountLogin類中,其代碼上面已經展示,回到testAccountLogin Case的操作,第二步操作為Account.getInstance().doLogin();即在登錄頁面執行登錄操作,其封裝在business層的Account類中,代碼片段如下:
public void doLogin(){    
LoginUiAction.*getInstance*().enterUsername(Config.*CURRENT_USERNAME*);    LoginUiAction.*getInstance*().enterPassword(Config.*CURRENT_PASSWORD*);    LoginUiAction.*getInstance*().clickLogin();    
HomeUiAction.*getInstance*().clickOnCancelGesture();    
Assert.*assertTrue*(LoginUiAction.*getInstance*().isLogin());
}

doLogin()方法中執行了“輸入用戶名;輸入密碼;點擊登錄按鈕;判斷登錄是否成功”操作,顯然這幾步操作都在UI層LoginUiAction類中。我們再查LoginUiAction.getInstance().
enterUsername()方法中做了什麽,代碼片段如下:

//輸入用戶名
public void enterUsername(String username){    
WebElement wl = DriverManager.*getInstance*().findElementById(packagename +    
      ":id/login_input_account");    
 wl.clear();   
 wl.sendKeys(username);
}

enterUsername()方法中,先根據id定位用戶名輸入框,然後清空輸入框,再輸入用戶名,這些操作都是Appium Client中提供的API。至此演示了登錄case的分層調用過程:cases—>business—>ui—>api。按照分層結構,只要業務邏輯不變,case維護任務主要放在ui層,上層無需改動,如此可極大減少case的維護代價。
在doLogin()方法中有一個“判斷登錄是否成功”的操作,斷言操作是自動化測試用例中必不可少的一部分,下面就開始介紹自動化測試用例的書寫規範。

自動化測試用例書寫規範及註意事項

一條case一般需要包括如下幾個要素:

  • 數據準備
  • 具體操作
  • 驗證點
  • 清楚操作結果

數據準備指提前準備測試賬號,假數據等;具體操作得按照業務測試同學提供的文本case來轉化;驗證點指自動化操作後,UI前後的變化點,比如登錄後,首頁會出現用戶名、總資產、財富分等控件,這些都是驗證點;清楚操作結果主要是基於case獨立性的考慮,盡可能做到每條case是獨立的,這樣某條case fail了,也不會對其他case造成影響,當然這樣會增加case的粒度,case穩定性會受影響,兩者之間可自行平衡。
下面介紹case開發的註意事項:

  1. 元素定位
    • ID(首選)
    • 文本
    • 控件類型
    • 坐標
  2. UI操作需要合理的sleep
  3. case獨立性
  4. 封裝常用操作
  5. 註釋及case前寫明操作步驟

元素定位有ID/文本/控件類型/坐標四種方式,推薦使用ID,因為只要某個控件還存在,其ID變化的可能性很小,若其位置發生了變動,case都無需維護。
下面給出查找元素ID的幾種方法:uiautomatorviewer/hierarchyviewer/源碼。uiautomatorviewer和hierarchyviewer工具在Android SDK中,配置好環境變量後,直接在命令行輸入命令即可打開圖形化工具,hierarchyviewer需要手機root,推薦使用uiautomatorviewer。如果有待測應用的源碼,也可以通過源碼來找ID,當然這種方式比較痛苦,但對熟悉業務及個人成長是有好處的,我最開始就是通過源碼來找ID的。
對於一些奇葩的情況,元素ID/文本/控件類型都無法定位時,使用坐標定位絕對好使,但不到萬不得已,盡量不用,因為用坐標定位就失去了兼容性,換個手機,case可能會執行失敗。
UI操作需要合理的sleep,經常由於網絡原因,UI加載需要一定時間,自動化操作過程中需要合理的sleep,sleep時間短了,case會失敗,長了又浪費時間。Appium框架中引入了隱式sleep方案,在初始化代碼中添加driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);即可,表示每步操作最多等待10s。
case獨立性前面已介紹,封裝及註釋容易理解,不做過多解釋。

功能自動化接入持續集成方案

功能自動化一般用於項目集中測試、回歸測試、dailybuild等,我們不可能通過IDE手動來運行case,一般可借助於jenkins或平臺化的方式來批量執行case。下面介紹如何將功能自動化接入jenkins。

技術分享圖片

接入jenkins主要用到了其定時和輪詢的功能,我們只要準備好構建jar(build.sh)和執行case(execCase.sh)的腳本,放入jenkins的Excute shell模塊,然後配置定時或輪詢的時間即可。build.sh和execCase.sh可參照第四部分介紹的auto_run.sh。具體執行過程如上圖所示:jenkins觸發自動化job;拉取構建站最新apk,拷貝至appium module的apps目錄下;構建測試工程,生成appium.jar(build.sh);批量執行case(execCase.sh),最後jenkins會輸出自動化的執行結果,但是輸出結果可視化程度不好,可自行開發生成報告腳本。至此詳細介紹了基於Appium的功能自動化開發全過程。

Appium做Android功能自動化測試