Android App自動(dòng)化性能測(cè)試探究

作者:彭海波

前言

Android App的性能測(cè)試是移動(dòng)測(cè)試過(guò)程中必不可少的一個(gè)環(huán)節(jié)并齐。在我們項(xiàng)目組內(nèi),性能測(cè)試的過(guò)程是這樣的例书,先設(shè)置測(cè)試場(chǎng)景混驰,然后一邊手工執(zhí)行場(chǎng)景攀隔,一邊通過(guò)工具獲取性能數(shù)據(jù),為了減少誤差账胧,一個(gè)場(chǎng)景一般重復(fù)執(zhí)行3-5次竞慢,測(cè)試完后將各種性能數(shù)據(jù)整理成報(bào)告。這個(gè)過(guò)程如果是一個(gè)經(jīng)驗(yàn)豐富的測(cè)試工程師去做治泥,可能要花半天到一天時(shí)間,而如果是一個(gè)新人去做遮精,甚至可能要花兩天時(shí)間居夹。而且有時(shí)候還會(huì)遇到败潦,剛測(cè)完,開(kāi)發(fā)說(shuō)優(yōu)化了性能准脂,又需要重新測(cè)試劫扒。這還只是一個(gè)Android端的時(shí)間,如果再加上iOS端狸膏,時(shí)間加倍沟饥。對(duì)于這樣一個(gè)現(xiàn)狀,我們急需探索出一種自動(dòng)化的方式提高性能測(cè)試的效率湾戳。本文接下來(lái)將要介紹云測(cè)試平臺(tái)在自動(dòng)化性能測(cè)試方面的探索和成果贤旷。

需求分析

作為一個(gè)負(fù)責(zé)任的測(cè)試開(kāi)發(fā)工程師,我們首先要搞清楚需求砾脑,即產(chǎn)品線的性能測(cè)試到底要怎么測(cè)幼驶,要測(cè)哪些數(shù)據(jù)。于是韧衣,我跟著參與了產(chǎn)品線的性能測(cè)試方案及標(biāo)準(zhǔn)的討論盅藻。了解到的性能測(cè)試需要采集的數(shù)據(jù)有:1.響應(yīng)時(shí)間;2.耗電量畅铭;3.CPU氏淑,內(nèi)存消耗,網(wǎng)絡(luò)流量硕噩;4.渲染幀率夸政。而這幾組數(shù)據(jù)需要覆蓋的測(cè)試場(chǎng)景大概有16種,如下表所示榴徐。

||響應(yīng)時(shí)間|CPU|內(nèi)存|耗電量|網(wǎng)絡(luò)流量|流暢度|
|-|-|-|-|-|-|
|首次APP啟動(dòng)到首頁(yè)加載完成|yes|yes|yes|no|yes|no|
|生活-繳費(fèi)|yes|yes|yes|no|yes|yes|
|生活-查違章|yes|yes|yes|no|yes|yes|
|生活-好醫(yī)生|yes|yes|yes|no|yes|yes|
|生活-添加房|yes|yes|yes|no|yes|yes|
|生活-添加車|yes|yes|yes|no|yes|yes|
|生活-添加隨手記|yes|yes|yes|no|yes|yes|
|側(cè)邊欄頁(yè)面加載|yes|yes|yes|no|yes|yes|
|生活繳費(fèi)-收銀臺(tái)功能|yes|yes|yes|no|yes|yes|
|查違章繳費(fèi)-收銀臺(tái)功能|yes|yes|yes|no|yes|yes|
|首頁(yè)滑動(dòng)|no|no|no|no|no|yes|
|Tab頁(yè)切換|no|no|no|no|no|yes|
|生活頁(yè)面滑動(dòng)|no|no|no|no|no|yes|
|理財(cái)頁(yè)滑動(dòng)|no|no|no|no|no|yes|
|App登錄狀態(tài)靜默30分鐘|no|no|no|yes|no|no|
|App非登錄狀態(tài)靜默30分鐘|no|no|no|yes|no|no|

方案調(diào)研

UI自動(dòng)化與性能測(cè)試相結(jié)合

在Android端守问,獲取性能數(shù)據(jù)的方式有很多種,可以通過(guò)開(kāi)源工具采集坑资,可以通過(guò)adb命令獲取耗帕,也可以調(diào)用系統(tǒng)API獲取。各種方法采集的數(shù)據(jù)基本差別不大袱贮,重點(diǎn)是場(chǎng)景操作和數(shù)據(jù)整理比較麻煩仿便。既然要提高測(cè)試效率,那最好的方式肯定是通過(guò)自動(dòng)化來(lái)解決攒巍。自動(dòng)化的框架有很多種嗽仪,這里就不一一做討論了,我們采用的是Appium框架柒莉,但是做了二次封裝和改進(jìn)闻坚,易用性更好。關(guān)于云測(cè)試平臺(tái)UI自動(dòng)化功能的使用方法介紹兢孝,大家可以參考云測(cè)試平臺(tái)幫助文檔一文窿凤。

測(cè)試方案

對(duì)于CPU仅偎,內(nèi)存,F(xiàn)PS雳殊,流量這幾組數(shù)據(jù)橘沥,云測(cè)平臺(tái)早就可以提供測(cè)試了。通過(guò)一個(gè)與UI自動(dòng)化并行的線程來(lái)發(fā)送adb命令獲取應(yīng)用的性能數(shù)據(jù)夯秃。我們需要解決的是如何設(shè)置性能測(cè)試的起點(diǎn)和終點(diǎn)座咆,還有一個(gè)場(chǎng)景多次重復(fù)測(cè)試的問(wèn)題。
那么我們需要想辦法測(cè)試響應(yīng)時(shí)間仓洼,我們采用的方案是設(shè)置一個(gè)待加載頁(yè)面的基準(zhǔn)控件介陶,利用Appium的元素查找功能一直循環(huán)查找,一旦控件找到了衬潦,則認(rèn)為頁(yè)面加載成功斤蔓,這中間的時(shí)間差即為頁(yè)面響應(yīng)時(shí)間。這個(gè)時(shí)間雖然沒(méi)有代碼插樁準(zhǔn)確镀岛,但誤差范圍基本控制在100ms內(nèi)弦牡,對(duì)整體測(cè)試結(jié)果的影響不大。

方案實(shí)施

開(kāi)啟一個(gè)性能測(cè)試的線程

關(guān)于獲取方式前面也介紹了漂羊,話不多說(shuō)驾锰,直接上代碼:

@Override
 public void run() {
  // TODO Auto-generated method stub
  this.running = true;
  while (running) {
   String time = String.valueOf(System.currentTimeMillis());
   
   time = CalendarDate.GetCurrentTime();
   //獲取內(nèi)存數(shù)據(jù)
   int [] memArray = AndroidPerformanceTools.getMemoryInfo(androidPerformance.getPkgname(), androidPerformance.getDevice());
   int totalMem = memArray[0];
   int appMem = memArray[1];
   //獲取CPU數(shù)據(jù)
   int cpuUsage = AndroidPerformanceTools.getCPUInfo(androidPerformance.getPkgname(), androidPerformance.getDevice());
   //獲取FPS
   float fps = AndroidPerformanceTools.getFPSInfo(androidPerformance.getPkgname(), androidPerformance.getDevice());
   //獲取流量數(shù)據(jù)
   long [] trafficArray = AndroidPerformanceTools.getTrafficInfo(androidPerformance.getPkgname(), androidPerformance.getDevice());
   long totalTrffic = trafficArray[0];
   long recTraffic = trafficArray[1];
   long sndTraffic = trafficArray[2];
   
   //數(shù)據(jù)初始化
   if (this.androidPerformance.getAndroidPerformanceData().getInittotal() == -1
     && totalTrffic > 0) {
    this.androidPerformance.getAndroidPerformanceData().setInittotal(totalTrffic);
    this.androidPerformance.getAndroidPerformanceData().setInitrec(recTraffic);
    this.androidPerformance.getAndroidPerformanceData().setInitsnd(sndTraffic);
   }
   
   //匯總數(shù)據(jù)
   MemInfo memInfo = new MemInfo(time, totalMem, appMem);
   FPSInfo fpsInfo = new FPSInfo(time, fps);     
   CPUInfo cpuInfo = new CPUInfo(time, cpuUsage);
   TrafficInfo trafficInfo = new TrafficInfo(time, totalTrffic, recTraffic, sndTraffic);
   this.androidPerformance.getAndroidPerformanceData().getCpuinfolist().add(cpuInfo);
   this.androidPerformance.getAndroidPerformanceData().getMeminfolist().add(memInfo);
   this.androidPerformance.getAndroidPerformanceData().getTraffinfolist().add(trafficInfo);
   this.androidPerformance.getAndroidPerformanceData().getFpsinfolist().add(fpsInfo);
   
   //設(shè)置采集間隔時(shí)間
   try {
    Thread.sleep(this.sleepTime);
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }
設(shè)置開(kāi)始和結(jié)束的點(diǎn)

由于UI自動(dòng)化的執(zhí)行每次都是從啟動(dòng)App開(kāi)始,但性能數(shù)據(jù)的收集卻是針對(duì)具體某個(gè)場(chǎng)景的走越,比如進(jìn)入生活繳費(fèi)頁(yè)面椭豫。因此為了方便設(shè)置性能測(cè)試的開(kāi)始和結(jié)束時(shí)間,我們?cè)赨I自動(dòng)化腳本的錄制action中加了兩個(gè)字段:startPerformance和stopPerformance旨指。用戶可以在腳本的任意位置加入這兩個(gè)action赏酥,但主要先后關(guān)系,而且需要配對(duì)使用谆构。當(dāng)我們?cè)趫?zhí)行腳本的時(shí)候裸扶,遇到startPeformance,就啟動(dòng)性能測(cè)試線程搬素,然后遇到stopPerformance呵晨,則停止線程,并保存數(shù)據(jù)熬尺。一個(gè)完整的用于做性能測(cè)試的腳本如下所示:

腳本
重復(fù)執(zhí)行

對(duì)于有些場(chǎng)景摸屠,我們?yōu)榱藴p少誤差,通常需要重復(fù)執(zhí)行多次取平均值粱哼。因此季二,我們?cè)谛陆y(cè)試任務(wù)的時(shí)候,增加了設(shè)置重復(fù)次數(shù)的參數(shù)皂吮。測(cè)試任務(wù)會(huì)根據(jù)設(shè)置的參數(shù)戒傻,重復(fù)執(zhí)行相應(yīng)的次數(shù)税手。

設(shè)置重復(fù)次數(shù)
響應(yīng)時(shí)間的測(cè)試

頁(yè)面時(shí)間是通過(guò)計(jì)算開(kāi)始加載到某個(gè)關(guān)鍵元素出現(xiàn)的時(shí)間差來(lái)作為衡量的蜂筹,具體實(shí)現(xiàn)方式如下:


@BeforeMethod
 public void beforeTest() {
  createDriver(); 
  stepStartTime = System.currentTimeMillis();
  Log.logger.debug("hello");
 }

@Test
 public void runStep(){
     stepTestTime = System.currentTimeMillis() - stepStartTime;
     if(loadingtime == 0)
      loadingtime = stepTestTime;
     //設(shè)置結(jié)果
     if(task.getTasktype().equals("Android 性能測(cè)試")){
        JSONObject stepResult = new JSONObject();
        stepResult.put("uielement", testStep.getUi_element().find_method_value);
        stepResult.put("stepname", testStep.getUi_element().name); 
        stepResult.put("steptime", stepTestTime);
        stepResult.put("stepresult", "success");
        stepResultList.add(stepResult);
        //taskService.setTaskResultInfo(taskId, stepResultList.toJSONString());    
     }
}
數(shù)據(jù)匯總與統(tǒng)計(jì)

性能測(cè)試結(jié)束后需纳,我們將數(shù)據(jù)匯總并進(jìn)行簡(jiǎn)單的統(tǒng)計(jì),最終寫入文件艺挪,保存最為測(cè)試報(bào)告的數(shù)據(jù)支持不翩。這里簡(jiǎn)單列舉下CPU的統(tǒng)計(jì)代碼:

ArrayList<CPUInfo> cpuInfos = androidPerformanceData.getCpuinfolist();

JSONArray cpuTimeJsonArray = new JSONArray();
   JSONArray cpuDataJsonArray = new JSONArray();
   double avgCPU = 0.0;
   long total = 0;
   for (CPUInfo cpuInfo : cpuInfos) {
    total += cpuInfo.cpuUsage;
    cpuTimeJsonArray.add(cpuInfo.time);
    cpuDataJsonArray.add(cpuInfo.cpuUsage);
   }
   avgCPU = total/cpuInfos.size();

成果展示

整體性能報(bào)告
整體性能報(bào)告
詳細(xì)性能報(bào)告

詳細(xì)性能報(bào)告主要顯示每次執(zhí)行的響應(yīng)時(shí)間結(jié)果,CPU麻裳,內(nèi)存口蝠,流量消耗以及FPS的分布曲線。這里簡(jiǎn)單列舉其中兩項(xiàng)數(shù)據(jù)的截圖如下津坑。


頁(yè)面響應(yīng)時(shí)間
內(nèi)存分布曲線

總結(jié)

至此妙蔗,我們通過(guò)UI自動(dòng)化與性能測(cè)試結(jié)合的方式解決了Android App自動(dòng)化性能測(cè)試的問(wèn)題。而且從腳本的錄制疆瑰,到測(cè)試執(zhí)行眉反,報(bào)告的生成都已經(jīng)平臺(tái)化。測(cè)試人員只需要錄制非常簡(jiǎn)單的腳本穆役,即可一鍵得到相應(yīng)的性能測(cè)試報(bào)告寸五。當(dāng)然,這種方式也有其缺點(diǎn)耿币,比如響應(yīng)時(shí)間的測(cè)試有些小誤差梳杏,錄制腳本有一定的學(xué)習(xí)成本。這些都是我們未來(lái)要優(yōu)化的方向淹接,歡迎大家提出改進(jìn)建議十性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市塑悼,隨后出現(xiàn)的幾起案子劲适,更是在濱河造成了極大的恐慌,老刑警劉巖拢肆,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件减响,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡郭怪,警方通過(guò)查閱死者的電腦和手機(jī)支示,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)鄙才,“玉大人颂鸿,你說(shuō)我怎么就攤上這事≡茆郑” “怎么了嘴纺?”我有些...
    開(kāi)封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵败晴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我栽渴,道長(zhǎng)尖坤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任闲擦,我火速辦了婚禮慢味,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘墅冷。我一直安慰自己纯路,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布寞忿。 她就那樣靜靜地躺著驰唬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腔彰。 梳的紋絲不亂的頭發(fā)上叫编,一...
    開(kāi)封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音萍桌,去河邊找鬼宵溅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛上炎,可吹牛的內(nèi)容都是我干的恃逻。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼藕施,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼寇损!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起裳食,我...
    開(kāi)封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤矛市,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后诲祸,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體浊吏,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年救氯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了找田。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡着憨,死狀恐怖墩衙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤漆改,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布心铃,位于F島的核電站,受9級(jí)特大地震影響挫剑,放射性物質(zhì)發(fā)生泄漏去扣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一暮顺、第九天 我趴在偏房一處隱蔽的房頂上張望厅篓。 院中可真熱鬧秀存,春花似錦捶码、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至澳盐,卻和暖如春祈纯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叼耙。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工腕窥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筛婉。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓簇爆,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親爽撒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子入蛆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容