標籤:
開始
通過Android測試之旅之JUnit(一)的學習,我們對JUnit的知識有了初步的認識。聰明的你是不是發現其實並沒有你想象的那麼難呢?這章我們繼續來瞅瞅JUnit還有什麼好玩的。今天我們用一個簡單的例子給大家進行展示,方便更好的理解。
Parameterized
我們先來看下面一個待測試類別PrettyTest:
public class PrettyTest { /** * 根據輸入值的大小返回字串 * @param a 輸入值 * @return 返回的字串結果 */ public String print(int a){ System.out.println("==========current input number is: " + a + "=========="); return a > 0? "大":"小"; }}
這個類相當的純潔。如果你看過上一章的內容,機智的你一定可以很快的寫出測試案例PrettyTest1:
public class PrettyTest1 { private static PrettyTest mTest; private static String expectedStrAbove; private static String expectedStrBelow; @BeforeClass public static void create(){ mTest = new PrettyTest(); expectedStrAbove = "大"; expectedStrBelow = "小"; } @Test public void testPrintAbove(){ int i = 1; String resultStr = mTest.print(i); System.out.println("==========testPrintBelow result string is:"+ resultStr + "=========="); Assert.assertEquals(expectedStrAbove,resultStr); Assert.assertNotEquals(expectedStrBelow,resultStr); } @Test public void testPrintBelow(){ int i = -1; String resultStr = mTest.print(i); System.out.println("==========testPrintBelow result string is:"+ resultStr + "=========="); Assert.assertEquals(expectedStrBelow,resultStr); Assert.assertNotEquals(expectedStrAbove,resultStr); }}
仔細看看,很滿意,很傲嬌。但是,有個小問題,如果我們要用多個值來測試怎麼辦?這裡只列舉了大於0和小於0的兩種情況。而在實際的開發中,一個方法中有的時候可能會有n多種情況。這個時候,Parameterized類的作用就能體現出來了。還是針對於PrettyTest,我麼來看看PrettyTest2這個測試案例:
@RunWith(Parameterized.class)public class PrettyTest2 { //-------初始化代碼省略------- @Parameterized.Parameter public int testNum; @Parameterized.Parameters public static Collection<Integer> initData() { List<Integer> data = new ArrayList<>(); data.add(-1); data.add(0); data.add(1); return data; } @Test public void test(){ String resultStr = mTest.print(testNum); System.out.println("==========testPrintBelow result string is:"+ resultStr + "=========="); if (testNum > 0){ //-------判斷是否與預期值相同------- }else{ //-------判斷是否與預期值相同------- } }}
代碼確實沒有少多少,我們再來看下運行結果。
運行了三次,把我們在initData方法中設定的資料都跑了一遍。然後test方法中隊各種情況進行相應的判斷,是不是覺得這樣效率提高了不少。其中@Parameter註解可以設定需要測試的公開變數,而@Parameters註解則是設定一個裝滿測試資料的集合。如果你不想使用@Parameter註解,還可以通過建構函式來設定。代碼如下:
private int testNum; public PrettyTest2(int num){ this.testNum = num; }
測試結果和之前的測試一模一樣。
RulesJUnit
稍微回味一下,我們繼續探索。Rules註解是個非常有意思註解,我們可以通過該註解在測試案例中類比我們需要的行為,聽這話有點昏昏的,別急,繼續往下看。因為測試的情境有很多,JUnit為我們定義了很多非常有用的Rules。比如說TemporaryFolder這個類,這個類就是JUnit為我們提供在測試過程中建立檔案夾的類,當測試結束之後,檔案夾會自動刪除。上代碼:
public class RuleTester { private static File testFile = null; @Rule public TemporaryFolder mFolder = new TemporaryFolder(); @Before public void before(){ System.out.println("----------method before testFile is: "+ testFile + "----------"); Assert.assertNull(testFile); } @Test public void test(){ try { testFile = mFolder.newFile("myfile.txt"); boolean flag = testFile.exists(); System.out.println("----------method test testFile exists flag: "+ flag + "----------"); Assert.assertTrue(flag); } catch (IOException e) { Assert.fail("exception is:"+e.getMessage()); } } @AfterClass public static void after(){ boolean flag = testFile.exists(); System.out.println("----------method after testFile exists flag: "+ flag + "----------"); Assert.assertFalse(flag); }}
先看下顯示結果。
事實證明確實在測試方法的過程中建立過一個檔案,並且在測試案例結束的時候檔案被刪除了。是不是很神奇?很溜?當然,如果你有興趣一步步的跟進去看源碼,你就會發現這其實就是對一個檔案建立,使用以及刪除的一個過程。TemporaryFolder類繼承了ExternalResource類,而ExternalResource類實現了介面TestRule中的apply方法。這裡不貼代碼是不是有點繞,別急,我們先繼續往下走,走完再來回頭看。
自訂
上面提到了TestRule這個神奇的介面,我打算寫一個內建日誌列印的Rule,裡麵包含一個列印訊息的方法。在控制台可以看到Rule被調用的過程。好,開始行動!
public class LogRule implements TestRule{ private Statement mBase; @Override public Statement apply(Statement base, Description description) { this.mBase = base; return new LogStatement(base); } public void print(String message){ System.out.println("LogRule message is:" + message); } public class LogStatement extends Statement{ private final Statement base; public LogStatement(Statement base) { this.base = base; } @Override public void evaluate() throws Throwable { System.out.println("method evaluate before"); try{ base.evaluate(); }finally { System.out.println("method evaluate after"); } } }}
在這個自訂的Rule中,我們重寫了apply方法,添加了print方法,並且重寫了Statement類中的evaluate方法,在這個方法前後我們加了日誌的列印。下面來看下在測試案例中的調用和輸出結果。
public class LogRuleTest { @Rule public LogRule mLogRule= new LogRule(); @Test public void test(){ String message = "method test"; mLogRule.print(message); }}
調用是不是很方便,現在在回頭看看之前我們講到的檔案建立,使用和刪除的過程,是不是一下子就恍然大悟了。那句俗話怎麼說的,車到山前必有路,船到橋頭自然直。
Categories
看到這裡有點累了吧,別急,快結束了。在上篇我們介紹過了SuiteClasses來選擇需要測試的測試案例。而Categories這個註解則可以幫你更上一層樓。為什麼這麼說呢,因為這個註解可以給你測試案例中的測試方法進行歸類。閑話不多說,上代碼!
public class ICategories { public interface First { } public interface Second { }}
public class CategoriesA { @Test public void a() { System.out.println("------class CategoriesA method a called------"); } @Category(ICategories.First.class) @Test public void b() { System.out.println("------class CategoriesA method b called------"); }}
@Category({ ICategories.First.class, ICategories.Second.class })public class CategoriesB { @Test public void c() { System.out.println("------class CategoriesB method c called------"); }}
有三個檔案,希望大家不要看暈了。我們首先定義兩種測試的類型,分別是介面First和Second。然後通過註解@Category對不同測試案例中的不同方法進行標註,最後選擇自己需要測試的內容。其中測試類別CategoriesA的a方法沒有任何標誌,b方法註明了First介面。CategoriesB測試案例上註明了First和Second兩個介面。我們接著往下看:
@RunWith(Categories.class)@Categories.IncludeCategory(ICategories.First.class)@Categories.ExcludeCategory(ICategories.Second.class)@Suite.SuiteClasses({ CategoriesA.class, CategoriesB.class })public class CategoriesTest {}
我們運行CategoriesTest這個測試案例。結果如下:
僅僅運行了CategoriesA的b方法,想必聰明的你已經知道了,@IncludeCategory註解表示我們需要包含哪個介面,@ExcludeCategory註解則表示我們剔除哪些介面。所以包含First介面而不包含Second介面的方法只有CategoriesA的b方法。好啦!你可以邊回味今天的知識邊休息啦。
結尾
到這裡,我們Android測試之旅之JUnit的全部內容就已經結束啦。不知道這些內容是否可以協助您解決問題。當然,有什麼問題可以隨時留言,我一定會積極回答的。
Android測試之旅之JUnit(二)