其實去年年末我就說過不少公司功能測試都作的差很少了,接下來就開始折騰什麼性能測試啊,安全測試啊,持續集成啊,Hybrid
啦等等。果不其然,最近不少測試同窗開始問我性能相關的問題。固然咱們專業點來說這個叫作專項測試
,那麼專項測試其實也是區分什麼人去作,工具組的人也在作,業務組的人也在作,只不過你們作的切入點會很不一樣。也許不少同窗也比較好奇我畢竟也去那麼多公司撕逼了,到底我平時在作什麼,怎麼作的。這邊那就簡單說下吧。css
嗯,我想一想我作了什麼。其實我如今就是,公司作到移動無線的應用專項測試就會想到我。而後以前一年我一我的作了持續集成,BDD,功能迴歸自動化,接口測試,靜動態掃描,從客戶端發起的接口測試等等。其實到如今爲止我仍是以爲我不知道我怎麼走上專項測試這條路的,貌似某一天公司說我來作就變成我來作了。java
其實別的先不說吧,咱們先來講下專項吧。專項這個東西關鍵在於幾點python
怎麼手動獲取數據
怎麼自動化獲取數據
怎麼分析數據
怎麼定位問題
怎麼優化android
固然我這裏仍是要吐槽一句,你們醒醒吧。花個幾年去研究UI 功能的自動化有意義嗎?要學的東西太多了,仍是醒醒吧。其實不少同窗關鍵是上面我說的這些都不知道,並且不只如此,還不知道的有shell
我什麼階段去作
每一個階段作什麼
作到什麼顆粒度
怎麼纔算完成了
。。。
也許還有不少,我也不想列了。其實我想說的是,其實也沒有什麼絕對,仍是看你的團隊,看你的項目,看你此次的目的,看項目階段等等。不一樣的時間都是不一樣的策略。好吧,咱們一個一個來吧。緩存
若是你有時間,而且項目是初級階段。那麼按照周爲單位須要去作一次專項評估。那麼這個過程當中你至少要mvn
或者gradle
或者pod install
等都success,不然搞個毛線。那麼這個過程當中須要去根據本週代碼的修改,從業務和技術角度去給出專項數據。這個數據到底給什麼,是根據此次新功能的定位,這個產品的定位來定的,不要去想着有什麼固定模式。好比騰訊qq和支付寶這兩個產品不管怎麼樣,在專項的技術上面不會差不少,可是專項測試想到達到的目的和測試的場景確定是天差地別的,明白?接着隨着功能完善以後,最終仍是須要作一次相似於全部模塊集成以後的專項測試,記住,顆粒度,範圍,數據怎麼獲取,徹底根據測試這個owner的策略來。安全
反過來,若是你沒有時間。那麼在success的基礎之上,你能夠在功能徹底完成以後直接來作一次集成專項評估。雖然效果不會差太多,可是這個方式的弊端在於,若是有問題,可能開發修改的時間就會不多,並且專項測試自己消耗的時間就會很長,因此最好是按部就班,而不是集成以後去作,每每時間來不及。網絡
真實場景模擬
好比此次有一個新項目,剛開始的項目,而後代碼編譯都ok了。那麼做爲新項目而言,咱們專項其實有不少,可是又不可能都作,這個受限於你的團隊的大小,以及功能的完整度。那麼可能先作最關鍵的,好比CR,好比功能體驗路徑的對比,好比內存消耗的對比,好比不一樣網絡下的數據對比,那麼這些都是相對一個移動應用來說最最重要的。剩下的能夠在以後的迭代中陸續去評估掉。顆粒度的話,仍是那句話,目前除了電量之外,剩下的數據基本上都是能夠經過各類方式(插樁,越獄,調用原生API,Hook等)拿到的。多線程
這個的確是一個比較逼人的問題。因此我纔會說評估不只僅是經過打出來的apk或者ipa來作的。而是在在項目迭代中持續去作的,那麼直到功能完成度100%的時候就差很少能夠作一輪完整的。那麼問題又來了,通常應用都還會繼續去改,怎麼辦呢?因此我說要CR啊。要結合業務重要性,功能重要性,代碼的修改來一塊兒評估每次修改所形成的影響。咱們不可能每次都去作一次專項,因此這個是必須會的技能。app
好了,這些解釋完了。那麼咱們繼續來看最最上面我提到的專項的關鍵點怎麼辦。這個我就拿我在西安寫的keynote爲例子吧。
好吧。這個不是我,是工具。我先澄清下。
使用不一樣的策略:其實就是根據本身的策略(各類操做比重不一樣)來制定腳本,包括也能夠簡單的二次開發,如今流行的作法就是去讀取當前全部的Views,而後去作遍歷,保證monkey能夠在每一個Activity上面都執行的到。
使用不一樣渠道商的腳本:如今各個渠道商都是有本身的monkey腳原本作測試的,若是不經過那麼同樣耶會被退回來,那麼與其這樣,不如提早去作。
修復全部的bug:那麼這個就是標準了,0 crash和 0 ANR。這兩個都是不容許的。
這個其實也是很重要的一個數據。那麼咱們在作以前首先先要來關注每一個機器的OS給每一個應用分配了多少內存佔用量,不然你怎麼知道數據是大是小呢。
這裏提供的這個方法是我推薦的,緣由是它給出的數據是個規則的矩陣,容易去作分析。可是也不是全部機器均可以去使用這個命令的,若是提示不能使用,大部分狀況就是由於沒有procrank
這個文件,本身Google下去下載一個push到手機裏便可。固然沒有root的貌似應該不行。
固然咱們也還有別的方式。
固然咱們還有別的方式,好比adb shell dumpsys
這個命令也能夠拿到比較全的內存信息
啓動性能分這兩種,畢竟有緩存和沒有緩存差異很大。那麼問題來了,雖然類型就兩種,可是緯度不少。
Native啓動時間
經過插樁或者grep ActivityManager或者ActivityLauncher都ok,固然更細節的話應該還有別的方式。
Hybrid啓動時間
這個詳見我QCon的PPT。這個插樁的地方就很是多了纔可以很準確的去獲取到
業務功能對應的網絡消耗時間
這個也是很是重要的。咱們不只僅要關注時間長短,更要關心每一個業務到底要調用多少個接口,其中css,js,png等是否根據網絡作了不一樣策略的調整,是否被壓縮了。時間數據僅僅是最終的一個展示。尼瑪,今天還有人和我說公司的東西我不敢放,老子就放了此次。相似於
到流量了。也是幾種方式吧
第一種經過ddms,具體怎麼用我就不教了,我不喜歡手把手。
第二種就是從OS中去獲取。和新方法以下:
String rcvPath = "/proc/uid_stat/" + uid + "/tcp_rcv"; String sndPath = "/proc/uid_stat/" + uid + "/tcp_snd";
固然未必必定要用java,能夠用shell或者python等腳本直接拿出來均可以啦。
第三種就是經過直接去調原生的接口來獲取,其實和ddms是同樣的道理。
@SuppressLint("ShowToast") public void getAppTrafficList() { PackageManager pm = getPackageManager(); List<PackageInfo> pinfos = pm .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS); for (PackageInfo info : pinfos) { String[] premissions = info.requestedPermissions; if (premissions != null && premissions.length > 0) { for (String premission : premissions) { if ("android.permission.INTERNET".equals(premission)) { int uId = info.applicationInfo.uid; long rx = TrafficStats.getUidRxBytes(uId); long tx = TrafficStats.getUidTxBytes(uId); if (rx < 0 || tx < 0) { continue; } else { Log.e(info.packageName.toString() + "Traffic", (rx + tx) + "kb"); } } } } } }
第四種就是架代理了。抓包去獲取流量大小和網絡數據。
固然還有最後一種,也是很重要的一種,那就是tcpdump
獲取數據和wireshark
來分析。具體不在這裏作教導了。
隨着如今應用使用頻率愈來愈高,應用發佈時候的大小也許還看得過去,可是用戶用着用着就不堪入目了。因此應用佔用量的增加也是我關注的點。
我編寫了一個應用來監控被測應用的三個數據的大小。核心代碼:
@SuppressLint("NewApi") public void queryPacakgeSize(String pkgName) throws Exception { if (pkgName != null) { PackageManager pm = getPackageManager(); try { Method getPackageSizeInfo = pm.getClass().getDeclaredMethod( "getPackageSizeInfo", String.class, int.class, IPackageStatsObserver.class); getPackageSizeInfo.invoke(pm, "<package name>", Process.myUid() / 100000, new PkgSizeObserver()); } catch (Exception ex) { Log.e(TAG, "NoSuchMethodException"); ex.printStackTrace(); throw ex; } } }
而後咱們就能夠看到一排一排的日誌啦
如今的確也有三種方式
1.功耗儀(安捷倫)
精準度最高,但費用消耗龐大,而且使用不方便。沒法作自動化
2.結合cpu等各類數據最終計算出電量消耗,單位是mA
精準度不如功耗儀,這個公式我這裏就不能給出了
3.經過消息的方式獲取。
精準度最低,單位是%。核心代碼
new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); int status = intent.getIntExtra("status", 0); // if (status == BatteryManager.BATTERY_STATUS_CHARGING) else } };
和啓動性能同樣,也有兩種。活動狀態和靜默狀態
據不可靠消息,stackoverflow上面其實有人已經說新版本的Android OS monkey就算增長這個參數在/misc/data 下面也不會有了。這個我還在進一步驗證中
那麼hprof
文件主要來源就是ddms了。可是爲何必定要強調場景,是由於咱們單獨去拿這個數據也沒有什麼太大意義。MAT分析的時候通常都是作diff的對比。而這個diff的對比必須基於場景之上。
咱們在測試以前總要知道標準吧。
一樣的,會根據不一樣場景進行測試和分析。一方面是和本身比,一方面是和競品去比。一旦有超出標準的,那麼繼續跟到代碼去分析。
一樣的須要去根據場景來分析。因爲這個功能是動態的。因此咱們須要去挑選場景。好比listview的場景,好比有tabbutton的場景,也就是說用戶在作滑動啊,滾動啊,切換界面等操做時候進行動態的數據的獲取。
基本上是被我放棄的東西了。因爲限制比較多。不過效果仍是很不錯的,也可以發現比較深層次的問題
這個須要本身作必定的二次開發了。IO性能對App總體性能提高會很大。最高能夠達到30%之多。
Android主要使用的是Xposed
,主要是hook被測應用中針對的方法,而iOS的話在越獄手機上去使用IOStringBuffer
這樣的方法去監控IO的頭文件,進行分析。無詳細案例
iOS的話我其實也就不想多說了。其實就兩個圖
最後仍是來講下自動化吧。其實最先我開源過一個python的,被吐槽的一塌糊塗。後來如今又從新造輪子造了一個java的。目錄如
其實方式很簡單,就是把咱們全部可以經過命令方式抓取的到的數據所有封裝起來,經過一個多線程的方式一邊在跑的時候一邊讀取數據一邊寫文件便可。
Monitor代碼以下:
public StartPerformaceMonitor(String filePath, String sn, String PackageName) { this.sn = sn; this.PackageName = PackageName; this.filePath = filePath; } public void stop() { this.isRunning = false; } @Override public void run() { cpu_index = CpuInfo.getCpuIndex(this.sn); vss_index = VssInfo.getVssIndex(this.sn); rss_index = RssInfo.getRssIndex(this.sn); String anyproxyData = ""; try { anyproxyData = AnyproxyInfo.StartCommand(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub while (this.isRunning) { try { try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // anyproxy anyproxyData = AnyproxyInfo.getALLData(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } resultList.put("anyproxyData", anyproxyData + ""); // traffic String traffic = TrafficInfo.getTrafficData(this.sn, this.PackageName); resultList.put("traffic", traffic); System.out.println("應用網絡消耗流量:" + traffic.substring(0, traffic.length() - 2) + "kb"); // totalsize = cachesize + datasize + codesize; String[] size = AppSizeInfo.getAppSizeData(this.sn, this.PackageName); resultList.put("cachesize", size[0]); resultList.put("datasize", size