iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 6
2
Mobile Development

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

Day06 - Mock 框架:Mockito

在上一篇,我們介紹了假物件Mock、Stub。用起來雖然方便,也解決了原本因為相依無法測試的問題。但每次要一這樣建mock或stub也太辛苦了,這時候我們就會用mock的框架來讓我們更方便的做到模擬物件的行為。

Mockito

Mockito 是一個Mock的框架。可以讓你用簡單的程式碼就建立一個模擬物件。用來模擬物件回傳值或驗證模擬物件的行為。

在buide.gradle 的dependencies加上mockito

dependencies {
    ...
    testImplementation "org.mockito:mockito-core:2.8.9"
}

在雨傘計算價錢的範例。我們建了一個StubWeather來回傳預期的天氣

class StubWeather :IWeather{
    //建立屬性,讓外部可設定假的天氣要回傳晴天或雨天
    var fakeIsSunny = false
    
    override fun isSunny(): Boolean {
        //回傳設定的假天氣
        return fakeIsSunny
    }
}

而使用Mockito的話,我們在寫測試時只需要這樣寫

1.使用mock() 建立模擬物件
2.使用when thenReturn 模擬回傳值。

@Test
fun totalPrice_sunnyDay(){
    val umbrella = Umbrella()

    //1. mock() 建立模擬物件
    val weather = mock(IWeather::class.java)
    //2. 模擬回傳值
    `when`(weather.isSunny()).thenReturn(true)

    val actual = umbrella.totalPrice(weather, 3,100)
    val expected = 270
    Assert.assertEquals(expected, actual)
}

是不是很簡單,不需要再寫StubWeather,輕易的達到讓weather.isSunny()回傳我們想要的結果

再來看上一篇的下單insertOrder發mail的範例,
我們寫了一個用來模擬發送Email的Mock。

class MockEmailUtil :IEmailUtil{
    // receiveEmail 用來記錄由sendCustomer傳進來的Email
    var receiveEmail:String? = null
    override fun sendCustomer(email: String) {
        receiveEmail = email
    }
}

測試程式:
1.產生模擬物件:mock(IEmailUtil::class.java)
2.使用verify驗證互動

@Test
fun insertOrderWithMockito() {

    val order = Order()
    val mockEmailUtil = mock(IEmailUtil::class.java)

    val userEmail = "someMail@gmail.com"
    order.insertOrder(userEmail, 1, 200, mockEmailUtil)

    //驗證是否有呼叫sendCustomer,並傳入正確的userEmail
    verify(mockEmailUtil).sendCustomer(userEmail)
}

以上我們用了Mockito來達到建立Mock、Stub的模擬物件,是不是很方便。

Mockito 其他功能

用annotations方式建立mock

你可以透過標示@Mock的annotations,並在@Before裡使用MockitoAnnotations.initMocks 來建立Mock,

class OrderTest {
    @Mock
    lateinit var mockEmailUt:IEmailUtil

    @Before
    fun setup()
    {
       MockitoAnnotations.initMocks(this)
    }
}

呼叫次數的驗證

//驗證呼叫1次
verify(mockEmailUtil, times(1)).sendCustomer(userEmail)

//驗證不能呼叫此方法
verify(mockEmailUtil, never()).sendCustomer(userEmail)

//驗證最少呼叫1次
verify(mockEmailUtil, atLeast(1)).sendCustomer(userEmail)

//驗證最多呼叫1次
verify(mockEmailUtil, atMost(1)).sendCustomer(userEmail)

any方法

如果你不在乎方法傳入的參數,可以使用any(),表示傳入任何參數皆可
verify(mockEmailUtil).sendCustomer(any())
Mockito 還可以模擬 interface的callBack、RxJava等等,但這就等到介紹Android的Unit Test時再來說明了。

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

參考:
https://site.mockito.org/

小技巧:
單元測試也是可以設定中斷點,執行Debug來方便除錯。
debug

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


上一篇
Day05 - 假物件 Stub、Mock
下一篇
Day07 - 單元測試小結
系列文
Android TDD 測試驅動開發30

尚未有邦友留言

立即登入留言