亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android單元測試 - 如何開始?

2019-11-09 17:53:07
字體:
來源:轉載
供稿:網友

轉載自:http://www.jianshu.com/p/bc99678b1d6e

回顧:

《談談為什么寫單元測試》

基本單元測試框架

java單元測試框架:Junit、Mockito、Powermockito等;Android:Robolectric、AndroidJUnitRunner、EsPResso等。

最開始建議先學習Junit & Mockito。這兩款框架是java領域應用非常普及,使用簡單,網上文章非常多,官網的說明也很清晰。junit運行在jvm上,所以只能測試純java,若要測試依賴android庫的代碼,可以用mockito隔離依賴(下面會談及)。

Junit官網Mockito官網

之后學習AndroidJUnitRunner,Google官方的android單元測試框架之一,使用跟Junit是一樣的,只不過需要運行在android真機或模擬器環境。由于mockito只在jvm環境生效,而android是運行在Dalvik或ART,所以AndroidJUnitRunner不能使用mockito。

然后可以嘗試Robolectric & Espresso。Robolectric運行在jvm上,但是框架本身引入了android依賴庫,所以可以做android單元測試,運行速度比運行在真機or模擬器快。但Robolectric也有局限性,例如不支持加載so,測試代碼也有點別扭。當然,robolectric可以配合junit、mockito使用。Espresso也是Google官方的android單元測試框架之一,強大就不用說了,測試代碼非常簡潔。Espresso本身運行在真機上,因此android任何代碼都能運行,不像junit&mockito那樣隔離依賴。缺點也是顯而易見,由于運行在真機,不能避免“慢”。

Robolectric官網Android-testing-support-library官網

其實espresso應該是幾款框架中最簡單的,但筆者還是建議先學習junit&mockito。因為新手很可能會因為espresso的強大、簡單,而忽略了junit做單元測試帶來的巨大意義。例如,前文提到“快速定位bug”、“提高代碼質量”,espresso慢,有違“快速”;用espresso不用修改工程任何代碼,這不利于提高代碼質量。

本文主要介紹junit和mockito,以及單元測試一些重要概念。


Junit

先給大家上兩段代碼壓壓驚:

public class Calculater { public int add(int a, int b) { return a + b; }}

AssertEquals

單元測試用例:

public class CalculaterTest { Calculater calculater = new Calculater(); @org.junit.Test public void testAdd() { int a = 1; int b = 2; int result = calculater.add(a, b); Assert.assertEquals(result, 3); // 驗證result==3,如果不正確,測試不通過 }}

以上是一個要測試的類Calculater和測試用例CalculaterTest。在IntellijAndroid Studio對類右鍵->run CalculaterTest,用例中所有被@org.junit.Test注解的方法,就會被執行。

run calculaterTestpass

測試通過。

如果代碼改成Assert.assertEquals(result, 4);,測試會失敗。

not pass

Verify

verify的作用,是驗證函數是否被調用(以及調用了多少次)。

public class CalculaterTest { @org.junit.Test public void testAdd2() { calculater = mock(Calculater.class); calculater.add(1, 2); verify(calculater).add(1, 2); // 驗證calculater.add(a, b)是否被調用過,且a==1 && b==2 // 測試通過 }}

是不是很簡單?

Mockito

官網這樣描述:

Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API.

大概意思是,Mockito是一個體驗很好的mocking框架,它可以讓你寫出漂亮、簡潔的測試代碼。

什么是mocking?下文會詳細說明。不如先讓你感受一下mockito代碼:

public interface IMathUtils { public int abs(int num); // 求絕對值}import static org.mockito.Mockito.mock;import static org.mockito.Mockito.when;public class MockTest { public static void main(String[] args) { IMathUtils mathUtils = mock(IMathUtils.class); // 生成mock對象 when(mathUtils.abs(-1)).thenReturn(1); // 當調用abs(-1)時,返回1 int abs = mathUtils.abs(-1); // 輸出結果 1 Assert.assertEquals(abs, 1);// 測試通過 }}

可以發現IMathUtils是一個接口,根本就沒有實現,用Mockito框架mock之后,IMathUtils.abs(-1)就有返回值1了。這就是Mockito神奇的地方!Mockito代理了IMathUtils.abs(num)的行為,只要調用時符合指定參數(代碼中指定參數-1),就可以得到映射的返回值。

Mockito的語法when...thenReturn...相當直觀,只要你小學有學英語^_^都能看懂。

讀者肯定認為Mockito用了Java代理,實際上要更高級一點,Mockito底層用了CGLib(github/cglib)做動態代理。


依賴隔離

依賴隔離,這是單元測試中一個非常重要的概念。一個單元的代碼,通常會有各種依賴。寫單元測試時,應該把這些依賴隔離,讓每個單元保持獨立。舉個例子:

public class Calculater { public double divide(int a, int b) { // 檢測被除數是否為0 if (MathUtils.checkZero(b)) { throw new RuntimeException("dividend is zero"); } return (double) a / b; }}public class MathUtils { public static boolean checkZero(int num) { return num == 0; }}

divide(a,b)計算a除以b,但被除數b不應該為0,所以用MathUtils.checkZero(b)驗證b==0。咋看這里好像沒什么問題,但是,如果MathUtils.checkZero里面的判斷邏輯寫錯呢?例如:

public static boolean checkZero(int num) { return num != 0; // bug }

如果不是num==0那么簡單,而是更復雜的算法呢?

因為Calculater引用的任何依賴,都可能出錯。更糟糕的是,如果用junit做單元測試,依賴里面可能是Android庫或者jni native方法,依賴方法一執行就會報錯。以上的各種原因,都會影響單元測試的結果。所以,我們對代碼做如下改進:

public class Calculater { IMathUtils mathUtils; public double divide(int a, int b) { if (mathUtils.checkZero(b)) { throw ... } return (double) a / b; }}public interface IMathUtils { public boolean checkZero(int num);}

我們可以在Calculater構造方法傳入IMathUtils派生類,又或者用setter。在項目執行代碼中,傳MathUtils,而單元測試時,可以寫一個MathUtilsTest繼承IMathUtils,傳給Calculater。只要保證MathUtilsTest.checkZero()正確就行。經過這么重構,Calculater就不依賴原來的MathUtils,單元測試時可以替換專門的實現,達到了依賴隔離的目的。

有同學會問,這樣豈不是每個依賴都要寫一個專門給單元測試的類嗎?這就等于拷貝多一份代碼,并且寫各種接口,而且不能保證單元測試的類一定正確。

說得很有道理。筆者為了盡量簡單地演示代碼,舉了一個非常簡單的例子。我們如何讓單元測試更簡潔,并且讓它閱讀起來更有意義呢?

Mock

為了更好地解決上述問題,我們引入Mock概念。Mock,翻譯為模擬,在單元測試mock可以模擬返回數據,也可以模擬接口/方法的行為。

什么是模擬行為?例如剛才mathUtils.checkZero(b),意義為:“當mathUtils調用checkZero(num)”時,判斷 num==0;又或者:“當調用checkZero(0)時返回true,num為其他值時返回false”。checkZero()是一個行為,返回的true、false就是數據。單元測試時,我們經常要讓方法/接口模擬某些行為,并得到模擬數據。

例如,需要測試a=2,b=1a=2,b=0調用divide(a,b)兩者結果分別是2,拋出錯誤,使用mockito框架讓mathUtils.checkZero()模擬行為,代碼如下:

public static void main(String[] args) { // 生成IMathUtils模擬對象 IMathUtils mathUtils = mock(IMathUtils.class); when(mathUtils.checkZero(1)).thenReturn(false); // 當num==1時,checkZero(num)返回false when(mathUtils.checkZero(0)).thenReturn(true); // 當num==0時,checkZero(num)返回true Calculater calculater = new Calculater(mathUtils); assertEquals(calculater.divide(2,1), 2); // 驗證 divide(2,1) 結果是2 try { calculater.divide(2, 0); // 預期拋出錯誤 throw new RuntimeException("no expectant exception"); // 如果divide沒拋錯,則此處拋錯 } catch (Exception e) { Assert.assertEquals(e.getMessage(), "dividend is zero"); // 驗證錯誤信息 }}

這段測試代碼可以運行通過!

代碼剖析:

mock(IMathUtils.class)生成IMathUtils類的模擬對象(稱mock對象)。這個mock對象調用任何方法都不會被實際執行;when(mathUtils.checkZero(1)).thenReturn(false),當調用checkZero(num)并且num==1,返回false,這里mockito模擬了checkZero()行為,并模擬了返回數據;所以,calculater.divide(2,1)返回結果2,calculater.divide(2, 0)拋出RuntimeException。

以上例子描述了,使用mockito模擬類方法和返回數據,通過mock隔離了CalculaterIMathUtils實現類的依賴,并通過單元測試,驗證了divide()的邏輯正確性。

條件覆蓋

無限條件

要驗證程序正確性,必然要給出所有可能的條件(極限編程),并驗證其行為或結果,才算是100%覆蓋條件。實際項目中,驗證邊界條件和一般條件就OK了。

還是上面那個例子,只給出兩個條件:a=2,b=1a=2,b=0,a=2,b=1是一般條件,b=0是邊界條件,還有一些邊界條件a=NaN,b=NaN等。要驗證除法正確性,恐怕得給出無限的條件,實際上,只要驗證幾個邊界條件和一般條件,基本認為代碼是正確了。

有限條件

再舉個例子:stateA='a0'、'a1', stateB='b0'、'b1'、'b2',根據stateA、stateB不同組合輸出不同結果,例如a0b0輸出a0b0,a0b1輸出a0b1,所以,共2*3=6種情況。這時,并不存在邊界條件,所以條件都是特定條件,并且條件有限。

這種情況在項目中很常見,以筆者經驗,建議單元測試時把所有情況都驗證一遍,確保沒有遺漏。


單元測試不是集成測試

集成測試

集成測試,也叫組裝測試、聯合測試。在單元測試的基礎上,將相關模塊組合成為子系統或系統進行測試,稱為集成測試。通俗一點,集成測試就是把多個(最少2個)組件合在一起,測試某個功能片段,甚至是單獨功能。

單元測試僅針對單元

在微信群很多同學問:“用Robolectric能不能請求網絡”,"Junit能直接請求服務器嗎"?

例如,我們使用MVP模式,如果我們想測試:調用PresenterA接口,請求真實網絡,并且返回數據后,解析成對象,并且根據返回數據執行對應邏輯。這明顯就是一個集成測試,而不是單元測試。PresenterA是一個單元,M層的Repository、DAO等是一個單元,更底層的sqlite第三方庫、網絡請求第三方庫(okhttp等) 也是單元.....組合了n個單元的測試,是集成測試。

Robolectric、Junit能否請求網絡?

包括筆者在內,很多同學一開始都會有這個疑問。

閱讀了本文第一部分,應該了解到robolectric、junit是運行在jvm,只要有一點點java開發經驗的同學,都知道jvm本身能連接網絡。如果你調用的方法所依賴的一切代碼,都不依賴Android庫(例如okhttp、retrofit),那99%都能在jvm上跑,并且能請求服務器。如果不幸有Android依賴,很大概率還是能在robolectric上跑的。

為什么robolectric不是100%能跑通測試呢?Robolectric僅支持API21及以下,并且不支持jni庫。因此,如果你的代碼依賴了API21以上接口或者jni接口,robolectric也無能為力。天?。≡趺崔k?

請讀者先不要沮喪,我們自有對策,不過要看讀者慧根了^_^!。前文“依賴隔離”提到,我們可以通過一定手段,把jni、android依賴隔離掉。咦?咱們的代碼是不是有救了?之后的文章,筆者會詳細給大家講解一下。

單元測試才是必要的

經過筆者指點,可能有讀者蠢蠢欲動去嘗試集成測試了.....且慢!說好的單元測試呢?集成測試看起來簡單,實際上由于依賴過多,很多時候很麻煩,而且運行慢;相比之下,單元測試則小巧、靈活得多,運行快,快速發現bug。在這方面,有一個理論Test Pyramid:

Test Pyramid

示意圖中,左箭頭表示速度,右箭頭表示開發成??梢钥吹?,單元測試速度比集成測試(Service,也叫Integration)、UI測試要快,并且開發成本也是最低。Test Pyramid告訴我們,應該花大部分精力去寫單元測試,其次才是集成測試、UI測試。

筆者建議,還是先老老實實做單元測試,有時間精力再做集成測試。


小結

本文介紹了幾個單元測試框架,介紹了junit、mockito初步使用,闡述了依賴隔離、mocking的概念,解答了"robolectric、junit能否請求網絡"問題。結合閱讀《談談為什么寫單元測試》,想必讀者對單元測試有了一個初步的了解。

如果讀者問筆者:“我的是小項目,是否有必要做單元測試?” 我很肯定地回答,任何項目都有必要做單元測試。至于單元測試是否耗費很多時間,或者效果不顯著,這要看使用者的編程經驗了,不能一概而論。

最后,叮囑讀者多敲代碼,真槍實彈地實踐單元測試??梢詮墓卷椖啃∫幠J褂?,形成自己單元測試風格后,就可以跟大范圍地推廣了。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲美女性生活视频| 欧美亚洲成人免费| 久久精品色欧美aⅴ一区二区| 九九精品在线观看| 成人日韩在线电影| 久久精品国产亚洲7777| 久久久久成人网| 亚洲欧美国产一本综合首页| 亚洲在线观看视频| 日韩精品视频免费| 欧美亚洲国产日韩2020| 两个人的视频www国产精品| 国产69久久精品成人看| 亚洲成人999| 国产精品www| 97在线观看视频国产| 91香蕉嫩草神马影院在线观看| 欧美亚洲午夜视频在线观看| 国产欧美在线看| 亚洲视频专区在线| 成人性生交大片免费看小说| 91中文在线视频| 亚洲一区美女视频在线观看免费| 欧美日韩亚洲视频一区| 97人人做人人爱| 久久精品久久精品亚洲人| 中文字幕不卡在线视频极品| 一本一本久久a久久精品综合小说| 国产精品欧美风情| 美女av一区二区| 日韩暖暖在线视频| 欧美噜噜久久久xxx| 欧美一区在线直播| 中文字幕精品久久| 日韩大片免费观看视频播放| 欧美成人合集magnet| 亚洲国产精品久久| 最近2019免费中文字幕视频三| 亚洲男女自偷自拍图片另类| 国产精品一区二区三| 国产亚洲aⅴaaaaaa毛片| 欧美有码在线视频| 亚洲国产天堂久久综合| 中文字幕精品www乱入免费视频| 国产日韩视频在线观看| 456亚洲影院| 久久久99免费视频| 日韩欧美综合在线视频| 成人在线观看视频网站| 欧美在线影院在线视频| 国产精品高清在线观看| 成人国产亚洲精品a区天堂华泰| 国产精品久久久久久久美男| 中文字幕久热精品在线视频| 久久久久北条麻妃免费看| 久久精品视频99| 91沈先生作品| 国产亚洲aⅴaaaaaa毛片| 欧美专区福利在线| 91人人爽人人爽人人精88v| 91av在线精品| 搡老女人一区二区三区视频tv| 亚洲欧美国内爽妇网| 国产精品日韩欧美大师| 欧美日韩国产二区| 国产亚洲视频在线| 欧美另类暴力丝袜| 欧美一级成年大片在线观看| 亚洲精品99久久久久中文字幕| 日韩精品高清在线| 国产精品尤物福利片在线观看| 国产91色在线免费| 欧美成年人视频网站| 亚洲国产精品高清久久久| 久久久久久九九九| 国产一区在线播放| 尤物99国产成人精品视频| 高清一区二区三区四区五区| 久久av红桃一区二区小说| 2019精品视频| 欧美日韩裸体免费视频| 中国人与牲禽动交精品| 国产精品欧美风情| 欧美极品美女视频网站在线观看免费| 中文字幕久热精品视频在线| 亚洲福利视频专区| 上原亚衣av一区二区三区| 久久久久久亚洲精品不卡| 午夜精品久久久久久99热| 国产精品www| 亚洲电影免费观看高清完整版在线| 国产精品久久久久久久久免费| 91精品国产高清久久久久久91| 久久亚洲精品国产亚洲老地址| 久久青草精品视频免费观看| 国产精品91免费在线| 69视频在线免费观看| 国产亚洲精品久久久久久777| 亚洲欧美日韩精品久久奇米色影视| 国产精品久久久久久久久久| 国产精品一区二区三| 国产精品mp4| 高跟丝袜欧美一区| 国产视频精品自拍| 色婷婷久久av| 在线观看日韩www视频免费| 欧美激情啊啊啊| 日韩欧美一区二区在线| 久久亚洲春色中文字幕| 日韩欧美大尺度| 日韩av毛片网| 91免费视频网站| 国产精品欧美一区二区三区奶水| 国产精品专区第二| 日韩中文字幕精品视频| 亚洲午夜小视频| 日韩国产欧美精品一区二区三区| 日韩成人久久久| 中文字幕日韩电影| 91最新国产视频| 亚洲欧美综合精品久久成人| 亚洲国产精品专区久久| 国产不卡av在线| 国产成人精品999| 亚洲国产黄色片| 国产精品免费一区豆花| 日本精品一区二区三区在线播放视频| 在线观看欧美成人| 性欧美xxxx视频在线观看| 懂色aⅴ精品一区二区三区蜜月| 国产精品视频久久久| 4438全国亚洲精品在线观看视频| 中文字幕日本精品| 国产精品日韩电影| 97成人精品视频在线观看| 欧美黑人视频一区| 欧美日韩性生活视频| 亚洲精品中文字幕女同| 色婷婷综合久久久久| 91国自产精品中文字幕亚洲| 亚洲偷熟乱区亚洲香蕉av| 国产精品久久久久久一区二区| 色妞色视频一区二区三区四区| 亚洲一级免费视频| 欧美影院在线播放| 亚洲欧美成人一区二区在线电影| 一本色道久久88精品综合| 在线观看欧美成人| 日韩av在线免费播放| 性欧美xxxx视频在线观看| 久久九九精品99国产精品| 国产脚交av在线一区二区| 欧美性猛交xxxx偷拍洗澡| 亚洲国产成人精品久久| 亚洲自拍偷拍色片视频| 美日韩精品免费观看视频| 国内精品久久久久久久久| 亚洲精品一区中文字幕乱码| 亚洲一区二区在线播放| 国产精品影片在线观看| 日韩精品免费在线| 美女福利视频一区| 久久精品国产精品| 亚洲精品91美女久久久久久久|