Android Studio下單元測試的本質(zhì)其實(shí)是根據(jù)通過書寫JAVA測試代碼,通過模擬用戶調(diào)用相應(yīng)的方法,或者使用者按下相應(yīng)的按鍵來驗(yàn)證我們的代碼的邏輯是否能達(dá)到預(yù)期的要求,如果所有的用例都能通過,則證明我們的邏輯滿足要求,否則,可以通過fail()函數(shù)(或使用Assert)進(jìn)行輸出錯(cuò)誤信息。
在進(jìn)行測試前我們首先需要了解一下幾個(gè)基本的概念:TestCase,TestSuite:
(圖1)
如圖1所示,TestSuite和TestCase都是繼承自Test接口,同時(shí),TestSuite的建立和使用依賴于TestCase實(shí)例,TestCase繼承自Assert類,因此TestCase中可以直接使用Assert中的相關(guān)方法,Assert類提供了幾個(gè)常用的判斷方法,Assert的類圖可以參照圖2:
(圖2)
TestCase:
在進(jìn)行單元測試的時(shí)候,在JUNIT4之前,我們需要測試的代碼所在的類一般都需要直接或者間接繼承自TestCase,對于我們創(chuàng)建的這個(gè)TestCase的子類,我們需要注意在我們這個(gè)類的測試流程,假設(shè)我們創(chuàng)建的TestCase子類中有兩個(gè)測試用例testMethod1和testMethod2,則執(zhí)行順序可以如圖3所示:
(圖3)
對于我們類中的兩個(gè)測試用例testMethod1和testMethod2,都會(huì)分別創(chuàng)建一個(gè)新的TestCase子類對象,并引起TestCase中的setUp和tearDown函數(shù)分別執(zhí)行一遍,因此,在進(jìn)行單元測試的過程中,我們可以在setUp當(dāng)中進(jìn)行一些初始化操作(如類的某些屬性的賦值操作),在tearDown中進(jìn)行一些掃尾工作(如類中某些對象所持有資源的釋放)。
一個(gè)簡單的Demo:
import junit.framework.TestCase;
public class TestDemo extends TestCase{
@Override
protected void setUp() throws Exception {
// TODO Auto-generated method stub
super.setUp();
System.out.println("setUp , hashCode = "+hashCode());
}
@Override
protected void tearDown() throws Exception {
// TODO Auto-generated method stub
super.tearDown();
System.out.println("tearDown,hashCode = "+hashCode());
}
public void testMethod1(){
System.out.println("testMethod1 , hashCode = "+hashCode());
}
public void testMethod2(){
System.out.println("testMethod2 , hashCode = "+hashCode());
}
}
運(yùn)行結(jié)果,如圖4所示:
(圖4)
有兩個(gè)test函數(shù),testMethod1和testMethod2,在生命周期的開始函數(shù)setUp以及結(jié)尾函數(shù)tearDown中分別產(chǎn)生了兩次不同的hashCode,根據(jù)Java語言中HashCode的概念我們可以知道這分別導(dǎo)致了我們的TestDemo類型的對象創(chuàng)建了兩次。因此如果測試的case增多,我們的TestDemo對象也會(huì)創(chuàng)建和case相同的個(gè)數(shù)。
對于測試用例testMethod1和testMethod2的函數(shù)聲明,在我們書寫用例函數(shù)的時(shí)候需要注意他們有一個(gè)共同的特點(diǎn):
1).訪問權(quán)限都是public;
2).返回類型都是void;
3).沒有參數(shù);
4).方法名以“test”開頭。
在使用單元測試的時(shí)候必須注意用例方法的生命格式,否則該用例將不會(huì)被執(zhí)行的到。
TestSuite:
對于suite這個(gè)英文單詞,從字面上可以理解為組合或者集合的意思,再加上通過圖1,我們發(fā)現(xiàn)TestSuite和TestCase都是實(shí)現(xiàn)自Test接口,這很容易讓我們想起JAVA設(shè)計(jì)模式中的合成模式的概念:即TestSuite可以認(rèn)為合成模式中的組,是一組TestCase對象的集合;而TestCase對象時(shí)這個(gè)合成模式中的葉子對象,并且,這些TestCase對象(葉子對象)和TestSuite(組對象)擁有共同的行為(run方法);這樣,可以保證當(dāng)用戶調(diào)用組對象TestSuite的run方法的時(shí)候,也會(huì)調(diào)用到TestCase對象的run方法。而事實(shí)上也確實(shí)是這樣,在使用JUnit3執(zhí)行測試的過程中,會(huì)首先創(chuàng)建TestSuite對象,在TestSuite對象的構(gòu)造方法中,會(huì)掃描TestCase子類的所有方法,并調(diào)用addTestMethod方法,在該方法中調(diào)用isPublicTestMethod方法判斷是否是待測的方法,若是會(huì)調(diào)用createTest方法,創(chuàng)建一個(gè)Test對象,并調(diào)用addTest方法加入到自己的集合中去,因此執(zhí)行過程中的TestCase子類都會(huì)以具體的test方法個(gè)數(shù)創(chuàng)建自身實(shí)例的個(gè)數(shù),并加入到TestSuite中,TestSuite的相對詳細(xì)的類圖如圖5所示:
(圖5)
一個(gè)簡單的例子了解一下TestSuite:
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
public class TestSuiteDemo extends TestSuite{
public static Test suite(){
//創(chuàng)建TestSuite對象
TestSuite suite = new TestSuite();
//為TestSuite添加一個(gè)測試用例集合,參數(shù)為:ClasstestClass
//通過參數(shù)可以知道,其實(shí)該參數(shù)就是TestCase的子類
suite.addTestSuite(TestDemo.class);
//創(chuàng)建具體的測試用例
Test test = TestSuite.createTest(TestDemo.class, "testMethod1");
//添加一個(gè)具體的測試用例
suite.addTest(test);
return suite;
}
}
執(zhí)行結(jié)果如圖6:
(圖6)
通過代碼和運(yùn)行結(jié)果,我們可以看出testMethod1執(zhí)行了兩次而testMethod2只執(zhí)行了一次,通過分析上述代碼得出testMethod1執(zhí)行兩次的原因是:第一次是在addTestSuite的時(shí)候?qū)⑵渥鳛橐粋€(gè)測試用例傳入到TestSuite中,第二次是通過addTest方法將用例加入到TestSuite中,因此在執(zhí)行的時(shí)候?qū)⑵鋱?zhí)行了兩遍,通過比較hashCode得出總共創(chuàng)建了三個(gè)TestCase對象的結(jié)論。
通過上述代碼,我們需要強(qiáng)調(diào)一下,如果我們想一次執(zhí)行一組TestCase實(shí)現(xiàn)類的測試,這個(gè)時(shí)候可以自定義TestSuite對象,將需要測試的TestCase實(shí)現(xiàn)類加入到TestSuite中去。我們需要了解TestSuite如何使用,其實(shí)TestSuite的使用也很簡單,在TestSuite的使用的時(shí)候,我們必須在TestSuite的實(shí)現(xiàn)類中,自定義suite方法,由于suite方法會(huì)通過反射調(diào)用,反射調(diào)用代碼如下:
public static Test testFromSuiteMethod(Classklass) throws Throwable {
Method suiteMethod= null;
Test suite= null;
try {
suiteMethod= klass.getMethod("suite");
if (! Modifier.isStatic(suiteMethod.getModifiers())) {
throw new Exception(klass.getName() + ".suite() must be static");
}
suite= (Test) suiteMethod.invoke(null); // static method
} catch (InvocationTargetException e) {
throw e.getCause();
}
return suite;
}
所以suite方法命名規(guī)則如下:
1).必須以“suite”方法命名;
2).suite方法的訪問修飾權(quán)限必須為static;
3).suite方法必須為靜態(tài)方法;
4).suite方法必須沒有參數(shù)。
總結(jié):
TestCase和TestSuite類是JUNIT中比較重要的兩個(gè)類,TestCase和TestSuite可以認(rèn)為是JAVA的合成設(shè)計(jì)模式在單元測試中的應(yīng)用,其實(shí)即便我們沒有自己聲明和創(chuàng)建TestSuite的子類,而且運(yùn)行的TestCase子類的過程中也會(huì)創(chuàng)建TestSuite類,并將要執(zhí)行的TestCase子類的實(shí)例對象添加到TestSuite中去執(zhí)行,其執(zhí)行過程可以如圖7所示:
(圖7)
在選擇JUnit執(zhí)行引擎的時(shí)候,便創(chuàng)建了TestSuite對象,并通過上面介紹TestSuite介紹過的addTestMethod,creatTest,addTest方法,將要測的TestCase類中的所有測試用例給掃描出來,并添加到待測列表中去。在執(zhí)行JUnit測試引擎的run方法時(shí)會(huì)調(diào)用TestSuite的的run方法,TestSuite在執(zhí)行自身run方法時(shí)會(huì)遍歷所有TestCase對象的run方法,同一個(gè)TestCase子類的run方法會(huì)根據(jù)自身所包含的測試用例個(gè)數(shù)被執(zhí)行相應(yīng)的次數(shù)。