2016年11月30日 星期三

Testing: Assert類別的使用

前言

前幾篇文章,讓我們對單元測試有了基本認識,從前面的練習可以稍微知道單元測試需要做哪些事情,可以透過Annotation幫我們在測試上提供程序上的輔助。目前我們都可以順利地對程式進行測試,接下來稍微認識一下Assert類別的用法,看看這個它能提供些什麼?

Assert類別

Assert類別提供許多靜態(static)方法,讓我們可以用來聲明某個特定的假設應該成立。為何要用這個類別?我們也可以在測試程式中加入if-else的判斷,就像先前提到的,我可以像手動測試一樣,寫些判斷,不是也能達到同樣目的?還記得單元測試應該就是要簡單、易讀?加上單元測試也不該出現判斷語句,因為當你出現判斷邏輯時,也表示你應該對它進行測試,這樣不就沒完沒了?所以我們應該使用Assert類別,並且當你測試失敗時,它會對你提出警告,藉由Assert類別在斷言失敗時給予你什麼訊息,好讓你對程式碼進行修正。

  • assertEquals
    • assertEquals(String message, Object expected, Object actual)
    • assertEquals(Object expected, Object actual)
    • assertEquals(String message, String expected, String actual)
    • assertEquals(String expected, String actual)
    • assertEquals(String message, double expected, double actual, double delta)
    • assertEquals(double expected, double actual, double delta)
    • assertEquals(String message, float expected, float actual, float delta)
    • assertEquals(float expected, float actual, float delta)
    • assertEquals(String message, long expected, long actual)
    • assertEquals(long expected, long actual)
    • assertEquals(String message, boolean expected, boolean actual)
    • assertEquals(boolean expected, boolean actual)
    • assertEquals(String message, byte expected, byte actual)
    • assertEquals(byte expected, byte actual)
    • assertEquals(String message, char expected, char actual)
    • assertEquals(char expected, char actual)
    • assertEquals(String message, short expected, short actual)
    • assertEquals(short expected, short actual)
    • assertEquals(String message, int expected, int actual)
    • assertEquals(int expected, int actual)
assertEquals我們很常用,主要是在斷言原生資料型態(primitive)與物件的相等,就類似我們平常在程式中使用obj1.equals(obj2)是同樣的。而有些assertEquals裡面可以置入訊息(message),也就是當你斷言失敗時,你希望提供什麼訊息,範例如下。

    @Test
    public void testAssertEquals() throws Exception {
        String s1 = new String("Test");
        String s2 = new String("Test");

        assertEquals(s1, s2);
    }

    @Test
    public void testAssertEqualsWithMessage() throws Exception {
        String s1 = new String("Test1");
        String s2 = new String("Test2");

        assertEquals("測試結果", s1, s2);
    }

junit.framework.ComparisonFailure: 測試結果
Expected :Test1
Actual   :Test2

  • assertSame
    • assertSame(String message, Object expected, Object actual)
    • assertSame(Object expected, Object actual)
  • assertNotSame
    • assertNotSame(String message, Object expected, Object actual)
    • assertNotSame(Object expected, Object actual)
assertSame/assertNotSame就類似我們平常使用(a == b) / (a != ib)是同樣的,是用來判斷兩個物件的實體或參照的記憶體是否相同,用以下案例就可以知道與assertEquals的差異了,程式碼如下。

public class AssertSameTest {

    @Test
    public void testAssertSame1() throws Exception {
        // 兩個物件各分配了一個記憶體空間,所以不一樣。
        String s1 = new String("test1");
        String s2 = new String("test1");

        assertNotSame(s1, s2);
    }

    @Test
    public void testAssertSame2() throws Exception {
        // s2參照s1同一個記憶體空間,所以一樣。
        String s1 = new String("test1");
        String s2 = s1;

        assertSame(s1, s2);
    }

    @Test
    public void testAssertSame3() throws Exception {
        // s1分配了一個記憶體空間,而s2使用字串池(String pool),所以不一樣。
        String s1 = new String("test1");
        String s2 = "test1";

        assertNotSame(s1, s2);
    }

    @Test
    public void testAssertSame4() throws Exception {
        // s1與s2都使用字串池,為了節省記憶體空間,只要字串池內的值相同,都是指向同一個記憶體空間,所以一樣。
        String s1 = "test1";
        String s2 = "test1";

        assertSame(s1, s2);
    }
}
  • assertTrue(boolean condition)
  • assertFalse
    • assertFalse(String message, boolean condition)
    • assertFalse(boolean condition)
其實看名稱就能知道斷言某個條件是否為true/false,而assertFalse同樣也能提供當斷言失敗時,所要顯示的訊息,程式碼如下。

public class AssertTrueTest {

    @Test
    public void testAssertTrue() throws Exception {
        boolean actualTrue = true;

        assertTrue(actualTrue);
    }

    @Test
    public void testAssertFalse() throws Exception {
        boolean actualFalse = false;

        assertFalse(actualFalse);
    }

    @Test
    public void testAssertFalseWithMessage() throws Exception {
        boolean actualTrue = true;

        assertFalse("測試失敗", actualTrue);
    }
}

junit.framework.AssertionFailedError: 測試失敗
  • fail
    • fail(String message)
    • fail()
fail就是讓你的測試案例聲明失敗,通常我們會在拋出例外時,在try區域使用,強制在沒有成功拋出例外時聲明,程式碼如下。

    @Test
    public void testFailWithMessage() throws Exception {
        fail("測試失敗");
    }

junit.framework.AssertionFailedError: 測試失敗

    @Test
    public void testFail() throws Exception {
        fail();
    }

junit.framework.AssertionFailedError
  • assertNotNull
    • assertNotNull(Object object)
    • assertNotNull(String message, Object object)
  • assertNull
    • assertNull(Object object)
    • assertNull(String message, Object object)
assertNotNull/assertNull也很容易理解,就是斷言物件是否為Null/Not Null,同樣都提供斷言失敗時,是否提供訊息,程式碼如下。

    @Test
    public void testNull() throws Exception {
        Object obj = null;

        assertNull(obj);
    }

    @Test
    public void testNotNullWithMessage() throws Exception {
        Object obj = null;

        assertNotNull("測試失敗", obj);
    }

junit.framework.AssertionFailedError: 測試失敗
  • assertArrayEquals
與assertEquals類似,也提供測試失敗時的訊息顯示,但用來判斷陣列裡面的內容是否相同,可省去單用assertEquals的麻煩,程式碼如下。

    @Test
    public void testAssertArrayEqualsTest() throws Exception {
        String[] array1 = new String[]{"John", "Xavier", "Same", "Justin"};
        String[] array2 = new String[]{"John", "Xavier", "Same", "Justin"};

        assertArrayEquals(array1, array2);
    }

小結

以上我們了解了Assert類別的用法,你可以把這些斷言方式看做是對各種不同條件的一種文法修飾,讓你在測試案例中,讓程式碼更清晰、易讀,而這些方法也簡單易學、方便、容易記憶。然而,上述的方法都有提供message參數,卻不建議帶入這個參數,原因在於訊息提供在測試框架中都有提供了,這個參數就有點像是程式碼裡的註解而已,反而顯得多餘,如果一定要使用這個參數,還不如將你的測試方法名稱取得更直覺或有意一些,以上只是為了示範而已。

Source Code

Github: https://github.com/xavier0507/UnitTestSample.git

沒有留言:

張貼留言