其實在程式考試結束後,連接著兩人關係的補課也該跟著結束了。
但是詩憶彷彿沒注意到這點似的,帶著早就準備好的甜食等在門口,看到最後一個考生離開馬上跑進教室。
「學姐!」
「嗯?」唯心本來正在擦拭白板,聽到聲音便偏過頭來看。
「學姐也累了吧?我這裡有餅乾和糖果唷。」
「系計中教室裡禁止飲食唷。」
「啊,不小心忘記了,那,等學姐出來再一起吃吧。」詩憶把食物收回包裡。
「好唷,我快收拾完了,對了,我聽說妳對MockK有興趣?」
「咦?學姐怎麼知道的?」
「當然是有人告訴我的呀。」唯心看了一下旁邊開著的電腦。「要不然我們先稍微研究一下再吃東西吧。」
打開MockK官網的網址和IDE,唯心注意到有版本搭配的說明。
「From version 1.10.0 MockK does not support Kotlin 1.2.*
,所以先檢查一下我們現在使用的kotlin版本吧。」於是乘載函式庫資訊的build.gradle.kts再次受到關注。
plugins {
kotlin("jvm") version "1.5.10"
}
「是1.5.10。」詩憶說。
「對呀,那我們就可以安心使用1.10.0後的最新版本了。」唯心說著,就把函式庫連著變數直接黏貼上去。
dependencies {
//...
testImplementation("io.mockk:mockk:{version}")
}
「學姐,稍等一下,我記得最新版本號在官網上面。」詩憶想要先離開IDE畫面。
「啊,不用那麼麻煩,來,我教妳用IDE拿最新版本的方法。」唯心點擊IDE下方的Dependencies。
打開了函式庫管理列表,點擊MockK的那列,選了版本表最上方的1.12.0。
build.gradle.kts裡的函式庫的版本號變數也自動替換上被選擇的版本。
dependencies {
//...
testImplementation("io.mockk:mockk:1.12.0")
}
「好了,那我們來看看官網的範例吧,畢竟這麼多函式不可能每個都試一遍呀。」唯心笑笑。
val car = mockk<Car>()
every { car.drive(Direction.NORTH) } returns Outcome.OK
car.drive(Direction.NORTH) // returns OK
verify { car.drive(Direction.NORTH) }
confirmVerified(car)
她看完範例後,很快就有了思路。「我覺得我們可以繼續用之前的飲料例子唷。」
class Drink(val name: String, val price: Int) {//飲料類別
fun freeAdd(): Boolean {//是否免費加料
return true
}
}
class DrinkOrder(private val drink: Drink) {//飲料訂單類別
var price = drink.price
fun add() {//加料
if (!drink.freeAdd()) {
price += 10
}
}
}
準備好飲料類別和飲料訂單類別後,唯心著手寫新測試。
import io.mockk.*
import kotlin.test.Test
import kotlin.test.assertEquals
class DrinkOrderTest {
@Test
fun add() {
val drink = mockk<Drink>()
every { drink.price } returns 60
every { drink.freeAdd() } returns false
val order = DrinkOrder(drink)
}
}
「咦?所以我們不用真的寫一個Drink物件,本來還在想飲料訂單類別不需要飲料名字,還要想飲料名字有點麻煩的。」詩憶睜大眼睛。
「是呀,因為飲料訂單依賴著外部傳入的飲料物件,所以要用mockk<T>(...)
的泛型概念建立飲料的假物件,不是本尊唷。接著用every
假造必要的函式和變數的回傳,就可以在飲料訂單裡幫飲料加料了。針對外部物件的假造,我記得是用Stub
來稱呼,但測試的主角是飲料訂單呢,所以接下來要驗證飲料訂單加料的結果。」
@Test
fun add() {
val drink = mockk<Drink>()
every { drink.price } returns 60
every { drink.freeAdd() } returns false
val order = DrinkOrder(drink)
order.add()
verify{ drink.freeAdd() }
assertEquals(60, order.price)
}
「為什麼要verify
函式drink.freeAdd()
呢?」
「喔,是為了確定前面的執行過程中有沒有呼叫到這個函式呀,因為order.add()
的前提條件需要drink.freeAdd()
。順帶一提,every
的位置很重要唷,如果妳沒提供飲料資料就投進飲料訂單,會出現錯誤唷。」
class DrinkOrderTest {
@Test
fun add() {
val drink = mockk<Drink>()
val order = DrinkOrder(drink)
every { drink.price } returns 60
every { drink.freeAdd() } returns false
}
}
出現錯誤no answer found for: Drink(#1).getPrice()
。因為DrinkOrder初始化所要取得drink.price
的值就是呼叫drink.getPrice()
。
class DrinkOrderTest {
@Test
fun add() {
val drink = mockk<Drink>()
every { drink.price } returns 60
val order = DrinkOrder(drink)
every { drink.freeAdd() } returns false
order.add()
verify{ drink.freeAdd() }
assertEquals(60, order.price)
}
}
「學姐,範例裡還有confirmVerified
,妳沒加進去呢。」
「嗯⋯⋯其實那是用來確認是不是驗證過所有的函式,沒驗證的會出現在Not verified calls
清單裡。順帶一提,只能驗證mock的對象,也就是說,我們只能confirmVerified(drink),不能confirmVerified(order),後者會出現錯誤can't find stub DrinkOrder@137ceb57
。」
「學姐,既然範例都說完了,我們可以去吃點心了吧。」詩憶覺得她的大腦和腸胃正在一同發出抗議。
「喔,差點都忘記這回事了呢,我們走吧。」唯心轉身提起包,卻沒留意到旁邊有個被留在桌上的水杯。
杯裡的水,灑了出來。
急忙伸出手試圖阻止水往電腦濺的詩憶,突然腦海中有什麼閃過,感覺眼前的場景好像在以前也發生過。