【友盟+】獨創SDK自動化測試框架,解決SDK測試痛點

做者:【友盟+】高級無線開發工程師 吳玉強、王飛java

爲了確保 SDK 線上運行的穩定性,咱們須要在開發後進行 SDK 測試,而爲了提升測試效率,並且在拓展新項目的同時能兼顧已有項目的穩定性,在有限的資源內解放測試人員到更緊急的項目中來,就須要一個自動化工具來完成工做,【友盟+】獨創自動化工具,可以自動傳不一樣參數、抓取輸出數據,並自動驗證數據準確性,輸出結果,保障項目順利穩定發佈。python

相對 App 的測試方案,市面上已經有很是多且成熟的 UI 級別的自動化測試框架,卻鮮有針對 SDK 提供的自動化測試方案,緣由是 SDK 屬於爲 App 提供服務的「插件」。一個 App 可接入一到多個 SDK 在內,而在項目中模塊化是很是廣泛的架構,因此 SDK 是針對細分功能提供服務的組件,有的提供數據服務、地圖服務或節省開發成本的組件等等,這隻能 SDK 開發者根據功能自行完成測試。shell

本篇說明的 SDK 測試方案是針對數據服務的 SDK 功能覆蓋,皆包含 SDK 的 API、網絡數據及緩存相關的邏輯測試,即非UI的純數據邏輯的覆蓋。json

本篇是自動化測試基礎上的延伸,相對安卓系統能夠便利的經過 adb 指令控制如App安裝、卸載、退出應用等「系統」級操做,iOS 在控制 App 層面上只能經過一些間接的手段完成上面幾點需求,爲了易於維護,在控制器中以有限狀態機模式進行了構造,以便於後續增長更多的操做狀態和測試用例。
圖片描述數組

1、測試框架概覽

一、測試框架

整個測試流程就以下面描述的有向圖,以 Pytest 驅動客戶端執行任務,而後將客戶端輸出的請求數據進行截取處理,然後驗證是否經過測試用例。緩存

二、Android端測試框架

Android可使用adb命令與app進行數據上的通訊,如發送廣播,啓動Activity等。同時也可使用shell命令對配置文件進行修改,再進行gradle編譯,實現對app級別參數的修改,從而完成不一樣參數對app程序影響的驗證。
圖片描述網絡

三、iOS端測試框架

iOS因爲系統特性,沒法如安卓系統靈活運用系統命令來操做 App 或 SDK,因此以一個 Socket 鏈接 Server 端進行通訊。
另外在 iOS 系統上又可利用 Runtime 的特性,將傳輸的字符串轉化爲 API 調用,這樣作的好處是將 Socket 模塊和 Runtime 解析模塊編入應用中就無需再次打包,只需 Python 端編好代碼和測試 case,全部的功能調用都由兩端約定的協議解析執行便可。
圖片描述架構

2、Android

一、SDK接口的驗證

對於集成SDK的app,若是須要在App運行時,觸發一個行爲,能夠經過廣播來實現。能夠根據action name完成對行爲類型的分類,根據caseid完成對行爲的區分。以下圖所示:app

根據上圖示例以下:框架

os.system( "adb shell am broadcast -a com.umeng.auto.track
--es param "" + str(es) + "" --ei caseId " + bytes(ei))

其中com.umeng.auto.track爲廣播的action name用以區分類別
ei爲一個int數,至關於圖中的caseid
es爲參數內容,參數的協議能夠自由定義,建議使用json類型,方便對不一樣類型的數據進行處理
這樣,咱們只須要在廣播中以ei寫一個switch語句,執行不一樣的行爲,若是測試不一樣參數的效果,可使用es傳遞內容。

二、Activity級別初始化的驗證

若是使用廣播,是沒辦法綁定生命週期,即若是SDK須要在Activity的onCreate()中進行一些類初始化操做,是無法進行控制的。因此對於這種狀況就須要使用adb命令中的啓動Activity命令,基本流程與廣播相似,可是caseid的處理在onCreate()中:

根據上圖示例以下:
圖片描述
os.system("adb shell am start -n " + self.pkgname + "/." + activity + " --es param "" + str(es) + "" --ei caseId " + bytes(ei))
其中pkgname爲包名,activity爲activity的名字es爲須要傳入的內容,ei爲一個int數,即caseId。
與廣播方式相似,只是將switch放到了onCreate中,根據ei和es進行相應的操做。

三、Application級別的驗證

以上說的兩種方式幾乎能夠涵蓋SDK測試的部分case,可是對於部分SDK,初始化須要在程序一啓動的Application中執行,這時上面的兩種方式顯然知足不了需求。
這時有兩套方案能夠應對。以下圖所示:
圖片描述

二次編譯

如上圖所示,左邊的部分,咱們能夠經過修改Java文件完成對Appliction中內容的修改,如在Application中會有一些靜態常量,使用python修改java文件中的常量,並從新運行:
def changeConstant(self, source,des):

path = os.path.join(os.path.dirname(sys.path[0]),  'autotestAndroid')
    gradle_path = os.path.join(path,'app','src','main','java','deep','autotest','utils','Constant.java')
    print '-----gradle_path----',gradle_path
    if os.path.exists(gradle_path):
        build_file = open(gradle_path, 'r+')
        lines = build_file.readlines()
        for i in range(len(lines)):
            line = lines[i]
            if ' '+source in line:
                arr = line.split('=')
                line = arr[0]+ '='+des+";\n"
                lines[i] = line
        build_file = open(gradle_path, 'w+')
        build_file.writelines(lines)
        p = buildprocess.CompileProcess(path)
        p.start()


    else:

print 'nonono='+ gradle_path

使用這種方式的好處是:
• 能夠直接修改Application中的常量,如AppKey等,不用管是否執行了Application的onCreate()
• 不用考慮外設狀況
• 一樣適配對AndroidManifest.xml的測試
缺點是:
• 須要綁定工程路徑
• 文件內容類型較多,容易出錯,代碼不具有通用性,有必定的二次開發難度
• 需使用gradle從新編譯,如工程較大,耗時較長

配置文件
除了上述方法,也能夠在Application中讀取一個SD卡配置文件,根據配置文件的協議進行對應的操做。每次只需更改配置文件的內容,並經過adb push放入SD卡指定路徑中,而後重啓App便可。
這樣作的好處是:
• 配置文件的協議能夠隨意定義,更靈活
• 配置文件可使用json格式,修改更簡單
• 只需推到SD卡,耗時更少
• 不須要綁定工程路徑
缺點是:
• 只能在Application的onCreate以後進行,侷限性較大。
• 依賴外設SD卡
• AndroidManifest的測試沒法使用。

3、iOS端SDK自動化測試流程

一、引入「守護」App

如「iOS端測試框架」所見,此時進行通訊只有一個應用,這個應用就是咱們用來測試 SDK 的 Demo,經過這個宿主咱們能夠觸發 SDK 提供的任何 API,經過 iOS runtime 咱們能夠觸發 SDK 的類方法、實例方法甚至是私有 API,但這寫都只侷限於一個應用「沙盒」內,如上面說到的安裝、卸載及 App 退出和切到後臺就無能爲力了,因此咱們引入了另外一個 Demo(Watch Demo),經過兩個 Demo 的協同操做知足「沙盒」以外的需求。
圖片描述

兩個 App 互相喚醒和通訊
圖片描述

如上面提到的,全部功能調用都基於約定的協議來執行的,協議的設計也是不斷新增的測試需求改造的。

二、業務協議

最初 Server 端與客戶端以測試用例的 case id 來區分須要觸發的事件,後來 case id 所表明的含義太多,並且客戶端也是以運行時不斷調用 Server 端發送指令的形式表現執行的具體功能,因此轉爲一條執行序列更加靈活及方便擴展。
一個測試用例可分爲多條執行序列,執行序列內的協議包含了須要進行的方法調用或事件的處理。
以 Dplus 爲例,以下數據包含了部分操做的執行序列:

"operations": {
    "$umeng_cloudayc_op9": {
        "arguments": {
            "param": [
                "$umeng_cloudayc_op*"
            ]
        },
        "type": "class",
        "class": "DplusMobClick",
        "method": "track:"
    },
    "$umeng_cloudayc_op5": {
        "arguments": {
            "param": []
        },
        "next": "$umeng_cloudayc_op9",
        "type": "class",
        "class": "DplusMobClick",
        "method": "clearSuperProperties"
    }
},
"type": "invoke",
"description": "401",
"first": "$umeng_cloudayc_op5"

因爲是針對 SDK API 測試的協議,因此協議內的格式以調用的類名、方法名及參數爲主,再加上部分細節參數加以說明,如 type 是 class 則調用類方法,是 instance 是示例方法。

須要注意的是,這個隊列的結構是個字典,以標識前綴 $umeng_cloudayc_op 做爲一個子事件的 key,value 則是其執行參數。並且能夠看到在參數 param 的 value 裏也有和子事件的 key 相似的值,這裏的設計也是爲了知足部分嵌套調用的需求。舉例來講,如此時須要經過一個接口驗證以前緩存的數據是否發送正常,就要分三步,第一存儲數據,第二將數據讀出,第三將第二步的結果做爲參數傳入最後調用的接口便可,這樣既能知足各類嵌套邏輯,又能實現遠程構造客戶端系統的實體對象做爲參數進行接口調用。

回到上面的字典的結構,實際上在以前的協議格式使用的是數組做爲執行序列的封裝格式,不過在實際應用中沒法知足靈活的要求,就如上面所說的組合的調用邏輯,有部分子事件是被動調用的,經過在其餘事件內的參數檢測來觸發調用,若是是數組則沒法控制這個執行序列的依賴關係。採用字典後,增長啓動字段,在後續關聯的子事件內,都會說明下一個執行的子事件,若是某個子事件是做爲另外子事件的參數,則不會有 next 字段,由於它是被動觸發的,不在執行隊列以內。

在這個業務協議開發過程當中,不斷的根據測試需求進行改造、添加,從一開始的單一應用調用接口,到後面的多應用切換、先後臺切換以及應用斷開和重連,須要多套控制流程,在具體實現時,分散到了各個業務邏輯中,每增長一個控制都要兼容考慮是否會影響到其餘模塊,並且做爲一個自動化測試「框架」,提早梳理好核心部分的流程會讓以後更易於開發和維護,因此就引入了有限狀態機的概念進行構造。

三、有限狀態機

圖片描述

有限狀態機(Finite-state machine)可用於模擬不少事物邏輯,顧名思義,它是一個有限的狀態的處理邏輯,有下面幾個特徵:

狀態數是有限的
在當前時刻只有一種狀態存在
一個狀態在知足某個條件後會切換到另外一狀態
而有限狀態機總體能夠概括爲四個要素:現態、條件、動做以及次態。

現態指當前時刻所表現的狀態
條件又稱爲事件,即當前狀態在知足這個條件後會觸發一個動做,從而進行狀態裝換
動做即在現態知足條件後需觸發的一系列操做,動做完成後即狀態進行遷移。動做也能夠忽略,在某些狀況下,現態知足條件後,也無需執行任何動做就切換到新的狀態。
次態是相對現態而言,表示了條件知足後遷移的狀態,次態也能夠與現態相同。
根據業務邏輯的特性及複雜程度,合適的使用有限狀態機,可使得邏輯表達清晰、封裝及維護都很直觀和方便。當一個業務包含的狀態越多,就越適合使用優先狀態機進行封裝處理。
有限狀態機應用很是普遍,如電子電路、編譯器及網絡協議 TCP 協議狀態機等

須要注意的是要區分「動做」和「狀態」,若是將「動做」也視爲「狀態」會致使編寫狀態機時產生問題。

四、有限狀態機應用自動化測試

將業務邏輯應用到有限狀態機,前提是須要熟悉對應的業務,並將其中的狀態、動做和條件等抽離出來,而後再作進一步的劃分和關聯,構造出一個完整的有向圖。

在自動化測試中,有以下幾個關鍵詞:
啓動測試、監聽、主App鏈接、守護App鏈接、接口調用、進入後臺、進入前臺、應用退出、崩潰、斷開鏈接、重連等。

在平常開發中,若是遇到上面的」事件」,可能就順其天然的開始寫判斷、寫調用,可能不自覺的就寫出了一個「有限狀態機」,不過不會那麼嚴格的區分什麼是動做什麼是狀態,只要知足最後的結果就能達成目的。
但如今咱們有意識的利用有限狀態機進行劃分,分離出狀態和動做以及狀態遷移的條件。看上面的關鍵字,好像都是一個個「動做」,仔細看「監聽(中)」又多是一個狀態,但實際上咱們還得須要結合業務的理解再抽象出一些狀態,如「進入後臺」,則是跳轉到了守護 App,當前是控制守護 App 的狀態;如果「進入前臺」則守護 App 跳轉到了「主App」,是控制主 App 的狀態。

以下圖就用剛纔抽象出的關鍵詞構造了一個簡單的有限狀態機:
圖片描述
按圖說明:

一、如架構圖描述的,須要主App和守護App同時鏈接纔可執行測試二、在鏈接完成後,狀態直接遷移到等待測試指令的狀態,沒有任何動做三、有些組合狀態能夠合成一個狀態,如運行守護App狀態時可能主App斷開鏈接,也可能保持鏈接,因此區分爲兩態分別管理。四、當自動化測試框架啓動後,除了監聽兩個App同時鏈接,其餘狀態都是在已有App鏈接完成的前提下進行的,因此大部分時間是在執行測試case調用及App切換的。

相關文章
相關標籤/搜索