iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 3
2
Mobile Development

Android TDD 測試驅動開發系列 第 3

Day03 - JUnit 測試框架

這一篇將對JUnit來做更進一步的介紹。
JUnit是一個用在Java的單元測試框架,當然在Kotlin也可以在JUnit的框架來寫測試。

你可以參考我的出版書,內容更完整。
Android TDD 測試驅動開發:從 UnitTest、TDD 到 DevOps 實踐
天瓏網路書店連結:https://www.tenlong.com.tw/products/9789864344901

目前我們用到了JUnit的這兩個功能:@TestAssert.assertEqual
@Test:用來表達是一個測試的function
Assert.assertEquals(expected, actual):驗證expected、actual 是否相同

常用的Annotation:

@Before : 在每一個測試之前執行。
@After : 在每一個測試之後執行。
@BeforeClass : 在這個類別開始執行第一個測試之前。
@AfterClass : 在這個類別全部測試完執行。

重構:使用@Before來移除重覆程式碼

測試的程式碼也是需要重構的。延續上一篇的測試,我們應將測試程式應視為Production Code的一部份,兩者一樣重要。

下方程式碼的3個測試,可以發現每個測試都會先做val math = Math()。

@Test
fun addTest() {
    val math = Math()
    val actual = math.add(1, 2)
    val expected = 3
    Assert.assertEquals(expected, actual)
}

@Test
fun testNumber1LessNumber2_minimumShouldBeNumber1() {
    val math = Math()
    val expected = math.minimum(1,3)
    val actual = 1
    Assert.assertEquals(expected, actual)
}

@Test
fun testNumber2LessNumber1_minimumShouldBeNumber2() {
    val math = Math()
    val expected = math.minimum(3,1)
    val actual = 1
    Assert.assertEquals(expected, actual)
}

既然這樣,我們何不讓每個測試都先跑這一段,
新增一個方法setup()並加上@Before的annotation,讓每個測試之前都要執行此方法。
這樣就能確保每次測試之前 math都會重新再初始化Math()一次。
這樣是不是就讓程式碼再簡單了一點。減少了重覆的部分。

class MathTest {
    lateinit var math: Math

    @Before
    fun setup(){
        math = Math()
    }

    @Test
    fun addTest() {
        val actual = math.add(1, 2)
        val expected = 3
        Assert.assertEquals(expected, actual)
    }

    @Test
    fun testNumber1LessNumber2_minimumShouldBeNumber1() {
        val expected = math.minimum(1,3)
        val actual = 1
        Assert.assertEquals(expected, actual)
    }

    @Test
    fun testNumber2LessNumber1_minimumShouldBeNumber2() {
        val expected = math.minimum(3,1)
        val actual = 1
        Assert.assertEquals(expected, actual)
    }
}

請記得@Before是在每個測試之前都會執行,跟下方直接在測試類的Property做初始化的作法是不一樣的。

class MathTest {
   var math = Math()
}

@Before 雖然好用,但需要注意以下幾點

  1. 避免在Before初始化只有某些測試會用到的物件
  2. 避免在Before寫太複雜的程式碼,這會讓你在看測試程式碼時難以除錯
  3. 可以考慮使用工廠方法來進行初始化

Assert 常用種類

assertEquals 判斷2個物件是否相等
assertNotEquals 判斷2個物件是否不為相等
assertTrue 驗證是否為真
assertNull 驗證物件是否為null
assertNotNull 驗證物件是否不為null
assertArrayEquals 驗證陣列是否相同

使用情境

比對兩個物件是否相等時,應使用 AssertEqual 而非 AssertTrue

正確

Assert.assertEquals(string1, string2)

錯誤

Assert.assertTrue(string1 == string2)

不要使用反向的寫法

正確

assertTrue(condition)

錯誤

assertFalse(!condition)

使用AssertTrue,而非 AssertEqual的情境

正確

assertTrue(someBoolean)

錯誤

Assert.assertEquals(true, someBoolean)

使用AssertNull 而非 AssertEqual的情境

正確

assertNull(someNull)

錯誤

Assert.assertEquals(null, someNull)

其他常用

Ignore用來表示先忽略這個測試。有時程式還沒寫好時,可以暫時加上Ignore忽略。

@Ignore("not implemented yet")
fun test() {

}

範例下載:
https://github.com/evanchen76/TDD_JunitSample

參考
JUnit https://junit.org/junit4/
單元測試的藝術一書 8.2.3 具可維護性的設計來使用Setup方法

小技巧

在測試類別,按下command + N,可快速產生SetUp Function與TearDown Function。
setup

出版書:

Android TDD 測試驅動開發:從 UnitTest、TDD 到 DevOps 實踐


上一篇
Day02 - 第一個測試
下一篇
Day04 - 晴天9折,雨天沒折
系列文
Android TDD 測試驅動開發30

尚未有邦友留言

立即登入留言