摘要:所以,寫單元測試,就是給你的每個類的每個方法寫對于的測試方法。常見的單元測試框架有等等。那么我們給這個東西做單元測試的時候,不是測這一整個流程。叫做集成測試,而不是單元測試。那對于這個例子,單元測試是怎么樣的呢這個請看下一小節。
這是一系列安卓單元測試的文章,目測主要會cover以下的主題:
什么是單元測試
為什么要做單元測試
JUnit
Mockito
Robolectric
Dagger2
一個具體的app例子實踐
神秘的bonus
什么是單元測試首先需要介紹一下什么是單元測試。很多人像我一樣,本科并不是計算機專業出身的,如果在職的公司不要求做單元測試的話,可能對這個詞并沒有一個確切的概念。而即使是計算機專業出身,如果畢業以后寫的不多的話,可能對這個詞的含義也不是很清楚。從名字上看,單元測試是為了測試某一個代碼單元而寫的測試代碼。但是什么叫“一個代碼單元”呢?是一個模塊、還是一個類、還是一個方法(函數)呢?不同的人、不同的語言,都有不同的理解。一般的定義,尤其是是在OOP領域,是一個類的一個方法。在此,我們也這樣理解:單元測試,是為了測試某一個類的某一個方法能否正常工作,而寫的測試代碼。
我們舉一個例子說明一下,假如你有一個類,定義如下:
public class Calculator { public int add(int one, int another) { //為了簡單起見,暫不考慮溢出等情況。 return one + another; } }
那么為了測試這個Calculator類的add()方法,我們可以寫如下的單元測試代碼:
public class CalculatorTest { public void testAdd() throws Exception { Calculator calculator = new Calculator(); int sum = calculator.add(1, 2); Assert.assertEquals(3, sum); } }
這里的CalculatorTest是Calculator對應的測試類。而這里的testAdd()就是add()這個方法對應的測試方法。所以,寫單元測試,就是給你的每個類的每個public方法寫對于的測試方法。非public方法我們一般是不測試的,雖然可以通過反射等手段去做,但是一般看來,非public方法是這個類的實現細節,我們并不關心,我們只關心某一個public方法的輸入、輸出。
一般來說,一個方法對應的測試方法主要分為3部分,以上面的測試方法為例:
setup。一般是new出你要測試的那個類,以及其他一些前提條件的設置:Calculator calculator = new Calculator();
執行操作。一般是調用你要測試的那個方法,獲得運行結果:int sum = calculator.add(1, 2);
驗證結果。驗證得到的結果跟預期中是一樣的:Assert.assertEquals(3, sum);
一般來說,我們寫單元測試,會用到一些單元測試框架。常見的Java單元測試框架有JUnit、TestNG等等。在這個系列的文章中,我采用JUnit 4,這也是用的最多的一個測試框架。上面的第三部,Assert.assertEquals(3, sum); 用的就是JUnit里面的驗證結果的方法,最常見的就是調用Assert類的一些assert方法。除了上面用到的assertEquals,還有assertTrue, assertNotNull等等。關于JUnit,我會在后面的系列文章中專門介紹。
如何在一個android project里面運行單元測試我們知道,在一個android gradle project中,源代碼默認是放在src/main/java下面的。而對應的單元測試代碼則是放在src/test/java下面的,如下圖所示:
其中的package name可以隨意定,很多人喜歡跟src package name保持一致,我個人習慣在最后加上.test后綴,因為AndroidStudio太智能了,經常我需要重命名單元測試的package的時候,AndroidStudio會把src的package也給重命名了。
打開CalculatorTest,用鼠標右鍵點擊testAdd()方法,選擇Run testAdd(), 如下圖所示:
從圖中你可以看出,你可以按快捷鍵Ctrl+Shift+R快速運行,當然,這要求你的光標當前焦點是在這個方法內部的。如果你的焦點是在類內部,而不在某一個測試方法內部,那么Ctrl+Shift+R將運行這個測試類的所有測試方法。當然,你也可以通過右鍵點擊測試類名來運行這個測試類里面的所有測試方法。
運行結束以后,你會在底部的“Run”這個tab看到運行的結果,如下圖所示:
除了在AndroidStudio里面運行,你還可以在命令行通過gradle testDebugUnitTest,或者是gradle testReleaseUnitTest,分別運行debug和release版本的unit testing,這種方式可以一次性運行所有測試類的所有測試方法。 這種方式的運行結果如下圖所示:
每個test case的報告可以在project_root/app/build/reports/tests/debug/index.html 這個xml里面看到。大致如下圖:
這篇文章的代碼在github上,感興趣的可以clone下來自己試試。
單元測試不是集成測試這里需要強調一個觀念,那就是單元測試只是測試一個方法單元,它不是測試一整個流程。舉個例子來說,一個Login頁面,上面有兩個輸入框和一個button。兩個輸入框分別用于輸入用戶名和密碼。點擊button以后,有一個UserManager會去執行performlogin操作,然后將結果返回,更新頁面。
那么我們給這個東西做單元測試的時候,不是測這一整個login流程。這種整個流程的測試:給兩個輸入框設置正確的用戶名和密碼,點擊login button, 最后頁面得到更新。叫做集成測試,而不是單元測試。當然,集成測試也是有他的必要性的,然而這不是我們每個程序員應該花多少精力所在的地方。在這方面,有一個理論叫做Test Pyramid,如下圖所示:
Test Pyramid理論基本大意是,單元測試是基礎,是我們應該花絕大多數時間去寫的部分,而集成測試等應該是冰山上面能看見的那一小部分。
為什么是這樣呢?因為集成測試設置起來很麻煩,運行起來很慢,發現的bug少,在保證代碼質量、改善代碼設計方面更起不到任何作用,因此它的重要程度并不是那么高,也無法將它納入我們正常的工作流程中。
而單元測試則剛好相反,它運行速度超快,能發現的bug更多,在開發時能引導更好的代碼設計,在重構時能保證重構的正確性,因此它能保證我們的代碼在一個比較高的質量水平上。同時因為運行速度快,我們很容易把它納入到我們正常的開發流程中。
至于為什么集成測試發現的bug少,而單元測試發現的bug多,這里也稍作解釋,因為集成測試不能測試到其中每個環節的每個方面,某一個集成測試運行正確了,不代表另一個集成測試也能運行正確。而單元測試會比較完整的測試每個單元的各種不同的狀況、臨界條件等等。一般來說,如果每一個環節是對的,那么在很大的概率上(雖然不是100%),整個流程就是對的。所以,集成測試需要有,但應該是少量,單元測試才是我們應該花重點去做的事情。
那對于這個例子,單元測試是怎么樣的呢?這個請看下一小節。
兩種函數(方法),兩種不同的測試方式一個類的方法可以分為兩種,一種是有返回值的,另一種是沒有返回值的。對于有返回值的方法,我們要測起來比較容易,就跟上面的Calculator例子那樣,輸入相應的參數,得到相應的返回值,然后驗證得到的返回值跟我們預期的值一樣,就好了。
但是沒有返回值的方法,要怎么測試呢?比如說剛剛login的例子,點擊那個按鈕,會執行Activity的login()方法,它的定義如下:
public void login() { String username = ...//get username from username EditText String password = ...//get password from password EditText //do other operation like validation, etc ... mUserManager.performlogin(username, password); }
這個方法是void的,那么怎么驗證這個方法是正確的呢?其實仔細想想,這個方法也是有輸出的,它的輸出就是,調用了mUserManager的performLogin方法,同時傳給他兩個參數。所以只要驗證mUserManager的performLogin方法得到了調用,同時傳給他的參數是正確的,就說明這個方法是能正常工作的。
那怎么樣驗證這個Activity的login()方法,會調用mUserManager的performLogin方法呢?這里涉及到mock的概念,在后面的文章關于Mockito的使用的時候,會介紹到,這里簡單解釋下,那就是在測試環境下,我們會使用一套mock framework(Mockito),生成一個mock的UserManager然后賦值給mUserManager,因為這個mUserManager是通過mock framework生成的,所以這個mock framework可以驗證它的什么方法被調用了,參數是什么,等等。
小結上面講述了單元測試的定義,以及與集成測試的區別,一般來說,單元測試不會接觸到數據庫,不會接觸到網絡,不會接觸到一些復雜的外部環境,如果有的話,那可能是你測試的方式有誤,測試的粒度不夠“單元”,希望這篇文章能將這兩者的區別解釋清楚。
有任何意見或建議,或者發現文中任何問題,歡迎留言!
最后,如果你也對安卓單元測試感興趣的話,歡迎加入我們的交流群:
作者 小創 更多文章 | Github | 公眾號
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/65782.html
摘要:單元測試三單元測試框架的使用掘金我們寫單元測試,一般都會用到一個或多個單元測試框架,在這里,我們介紹一下這個測試框架。 Android 單元測試 (三):JUnit 單元測試框架的使用 - Android - 掘金 我們寫單元測試,一般都會用到一個或多個單元測試框架,在這里,我們介紹一下JUnit4這個測試框架。這是Java界用的最廣泛,也是最基礎的一個框架,其他的很多框架,包括我們后...
摘要:什么是單元測試單元測試是對程序的最小單元進行正確性檢驗的測試工作。編寫第一個單元測試單元測試主要使用是測試框架類庫的擴展庫,需要在中聲明測試依賴。目標代碼這里以一個簡單的中的例子來說明如何寫單元測試。TL;DR: 本文主要面向單元測試新手,首先簡單介紹了什么是單元測試,為什么要寫單元測試,討論了一下 Android 項目中哪些代碼適合做單元測試,并以一個簡單例子演示了如何編寫屬于你的第一個 ...
閱讀 2101·2021-11-18 10:02
閱讀 2850·2021-09-04 16:41
閱讀 1142·2019-08-30 15:55
閱讀 1405·2019-08-29 17:27
閱讀 1069·2019-08-29 17:12
閱讀 2535·2019-08-29 15:38
閱讀 2855·2019-08-29 13:02
閱讀 2831·2019-08-29 12:29