簡介
關(guān)于單元測試的工具調(diào)研可以參考:https://km.sankuai.com/page/365031302
在構(gòu)建本地單元測試的時(shí)候,JUnit4測試框架是java標(biāo)準(zhǔn)測試庫,junit測試框架可以讓你在編寫測試代碼的中進(jìn)行setup, 卸載, 和斷言等操作。構(gòu)建本地單元測試框架運(yùn)行不需要依賴于真實(shí)設(shè)備或者模擬設(shè)備,可以通過Robolectric庫來實(shí)現(xiàn)對(duì)Android系統(tǒng)框架的依賴,Robolectric庫是可以兼容junit庫的。
通過junt4進(jìn)行本地單元測試,不要依賴于android運(yùn)行環(huán)境,因此運(yùn)行速度更快,可以配合Truth庫快速測試某個(gè)方法是否符合預(yù)期的程序設(shè)計(jì)。在編寫測試代碼的時(shí)候,可以按照業(yè)務(wù)實(shí)現(xiàn)類來新建測試類,每個(gè)Junit4測試類對(duì)應(yīng)一個(gè)需要測試的業(yè)務(wù)代碼,每個(gè)被@Test注解的測試方法是需要被測試的public方法用于驗(yàn)證業(yè)務(wù)功能等。
測試環(huán)境搭建
在程序中引入junit庫,在moudule的build.gradle中配置,其中truth庫是為了斷言使用。
testImplementation 'junit:junit:4.12'
//斷言庫,替換junit的api
testImplementation "com.google.truth:truth:1.0.1"
在 Android Studio 項(xiàng)目中,將本地單元測試的源文件存儲(chǔ)在 module-name/src/test/java/ 目錄下,通常在新建完項(xiàng)目以后,該目錄會(huì)自動(dòng)創(chuàng)建,并且會(huì)存在一個(gè)默認(rèn)的測試用例代碼。
代碼目錄結(jié)構(gòu)如下:
module-name/src
├── androidTestjava (插樁單元測試、Espresso測試)
├── main/java (業(yè)務(wù)代碼)
├── test/java (本地單元測試)
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
常見API:參閱文檔 JUnit annotations 和 Android annotations.
@Before: 使用這個(gè)注解可以指定一段包含測試設(shè)置操作的代碼。測試類在每個(gè)測試執(zhí)行前調(diào)用這段代碼。你可以定義多個(gè)帶有@Before注解的方法, 但是在測試類中這個(gè)方法執(zhí)行的順序是不明確的。
@After: 這個(gè)注解指定一段代碼包含測試卸載操作。在每個(gè)測試方法執(zhí)行后調(diào)用這段代碼。你可以定義多個(gè)@After操作的代碼。使用這個(gè)注解來釋放內(nèi)存中的資源。
@Test: 使用這個(gè)注解標(biāo)識(shí)一個(gè)測試方法。 一個(gè)單獨(dú)的測試類可以對(duì)應(yīng)多個(gè)測試方法。
@Rule: @Rule通過復(fù)用的方法,可以允許你可以靈活的添加和修改每個(gè)測試方法。 在Android測試中, 此注解需要和Android測試支持庫中提供的測試規(guī)則類配合命用。比如 ActivityTestRule和 ServiceTestRule
@BeforeClass: 使用此注解來指定一個(gè)測試類的靜態(tài)方法只能調(diào)用一次。這種測試步驟對(duì)于耗時(shí)操作非常有用, 例如連接數(shù)據(jù)庫操作。
@AfterClass: 使用這個(gè)注解來指定一個(gè)靜態(tài)方法, 當(dāng)類中所有的測試方法都已經(jīng)運(yùn)行完成的時(shí)候調(diào)用。 這個(gè)步驟對(duì)釋放@BeforeClass塊中占用的資源非常有用。
@Test(timeout=): 一些注解支持在注解中設(shè)置變量值。 例如, 你可以指定一個(gè)測試的超時(shí)時(shí)間,如果一個(gè)測試開始并且沒有在指定的超時(shí)時(shí)間內(nèi)完成, 它自動(dòng)認(rèn)為校驗(yàn)失敗。超時(shí)時(shí)間的單位是毫秒, 如 @Test(timeout=5000)。
@Ignore:讓Junit忽略某些測試方法,可能是由于測試方法沒有完全編寫完成或者太過于耗時(shí)。
@Test(expected = NullPointerException.class):驗(yàn)證測試方法會(huì)拋出某些異常,在Junit測試中通過設(shè)置expected參數(shù)指定異常類型例如NullPointerException空指針異常,如果該測試方法拋出IllegalArgumentException異常則綠色passed,如果沒有拋出的話,則測試紅色failed失敗。
示例代碼:
獲取三個(gè)數(shù)中最大的那個(gè)數(shù):這個(gè)為了說明setup的操作,沒有把max方法寫成static類型。
public class MathUtil {
public int max(int a, int b, int c){
if(a > b){
if(a > c){
return a;
}else{
return c;
}
}else{
if(b > c){
return b;
}else{
return c;
}
}
}
}
測試代碼
測試代碼簡單分析:
@Before 注解的setup方法可以讓測試程序處理一些初始化操作,比如公有的對(duì)象初始化比如下面的代碼創(chuàng)建了mathUtil對(duì)象。,如果這個(gè)對(duì)象不單單是在一個(gè)測試方法用的對(duì)象話,這樣可以避免在每個(gè)測試類中都編寫創(chuàng)建該對(duì)象的邏輯。在運(yùn)行測試代碼的時(shí)候,首先會(huì)執(zhí)行被@Before注解的方法,注意:被@Before注解的方法,不是執(zhí)行一次,而是每個(gè)被@Test注解的方法執(zhí)行之前都是調(diào)用setup方法。
@After注解的方法,主要用于釋放資源,一般很少用到,比如文件關(guān)閉,數(shù)據(jù)庫斷開鏈接之類的。和@Before一樣,每個(gè)測試方法執(zhí)行完以后,也都會(huì)調(diào)用@After方法。
驗(yàn)證單元測試的結(jié)果,junit本身提供了一整套斷言功能,但是目前推薦使用Truth庫,下面用的就是Truth庫中的比較兩個(gè)int是否相等的邏輯。斷言的邏輯比較簡單,更多的API可以參考Truth庫
public class MathUtilTest {
MathUtil mathUtil;
@Before
public void setup() {
mathUtil = new MathUtil();
}
@Test
public void testMax() {
Truth.assertThat(mathUtil.max(1, 2, 3)).isEqualTo(3);
}
@After
fun releaseResource(){
}
}
測試結(jié)果
參考:https://blog.csdn.net/u011060103/article/details/107297512
總結(jié)
本文主要介紹junit的常見api和Truth斷言庫的簡單使用,整體來看junit是比較簡單的,可以針對(duì)一些public方法方法做單元測試并驗(yàn)證單元測試的結(jié)果是否符合預(yù)期。但是我們?cè)趯?shí)際業(yè)務(wù)開發(fā)的時(shí)候往往沒有這么簡單,主要涉及到以下幾個(gè)問題:
(1)類中存在大量的private方法,在實(shí)際業(yè)務(wù)開發(fā)中,public方法往往只是對(duì)外暴露沒有實(shí)際邏輯,實(shí)際邏輯都寫在private中,而junit不能直接測試private方法。
(2)對(duì)于沒有返回值的方法,junit無法斷言,這個(gè)時(shí)候我們有需要測試該void方法是否得到執(zhí)行。
(3)如果有的方法依賴于Android環(huán)境,比如content等。
解決以上的問題,需要借助于Robolectric 或模擬框架(如Mockito庫)來實(shí)現(xiàn)上述問題。