這篇會替Day21 ROOM -2 (番外)補上測試
首先新增
WordDaoTest在 androidTest底下
先做前置作業
WordDaoTest.kt
@RunWith(AndroidJUnit4::class)
class WordDaoTest {
val instantTaskExecutorRule = InstantTaskExecutorRule()
private lateinit var wordDao: WordDao
private lateinit var db: WordRoomDatabase
...
}
每次運行tests前要先創建DB
執行完後close DB
WordDaoTest.kt
class WordDaoTest {
...
@Before
fun createDb() {
val context = InstrumentationRegistry.getTargetContext()
//將db存在內存 當process結束時會清除
db = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java)
// 允許在主線程查詢 僅用於測試
.allowMainThreadQueries()
.build()
wordDao = db.wordDao()
}
}
每次執行完後清除
class WordDaoTest {
...
@After
fun closeDb() {
db.close()
}
}
其中
instantTaskExecutorRule 用途是為了避免 java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
錯誤
接著開始撰寫測試案例
WordDaoTest.kt
@Test
fun insertAndGetWord() = runBlocking {
val word = WordEntity("word")
wordDao.insert(word)
val allWords = wordDao.getAllWords()
Assert.assertEquals(allWords?.value?.get(0), word.word)
}
然後運行
此時結果應該會是fail的
原因是LiveData不是即時更新的
所以照著運行下來時allWords的數據還沒更新 是null
所以失敗
接著要寫一個擴展來等待數據更新
之後驗證資料是否正確
代碼如下
LiveDataTestUtil.kt
@Throws(InterruptedException::class)
fun <T> LiveData<T>.waitForValue(): T {
val data = arrayOfNulls<Any>(1)
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data[0] = o
latch.countDown()
this@waitForValue.removeObserver(this)
}
}
this.observeForever(observer)
latch.await(2, TimeUnit.SECONDS)
return data[0] as T
}
這段擴展會等待兩秒直到liveData回調
WordDaoTest完整代碼如下
WordDaoTest.kt
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.room.Room
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import com.ithome11.jetpackmvvmdemo.util.WordDao
import com.ithome11.jetpackmvvmdemo.util.WordEntity
import com.ithome11.jetpackmvvmdemo.util.WordRoomDatabase
import junit.framework.Assert
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class WordDaoTest {
//立即調用線程的背景操作
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
private lateinit var wordDao: WordDao
private lateinit var db: WordRoomDatabase
@Before
fun createDb() {
val context = InstrumentationRegistry.getTargetContext()
//將db存在內存 當process結束時會清除
db = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java)
// 允許在主線程查詢 僅用於測試
.allowMainThreadQueries()
.build()
wordDao = db.wordDao()
}
@After
fun closeDb() {
db.close()
}
@Test
fun insertAndGetWord() = runBlocking {
val word = WordEntity("word")
wordDao.insert(word)
val allWords = wordDao.getAllWords().waitForValue()
Assert.assertEquals(allWords[0].word, word.word)
}
@Test
fun getAllWords() = runBlocking {
val word = WordEntity("aaa")
wordDao.insert(word)
val word2 = WordEntity("bbb")
wordDao.insert(word2)
val allWords = wordDao.getAllWords().waitForValue()
Assert.assertEquals(allWords[0].word, word.word)
Assert.assertEquals(allWords[1].word, word2.word)
}
@Test
fun deleteAll() = runBlocking {
val word = WordEntity("word")
wordDao.insert(word)
val word2 = WordEntity("word2")
wordDao.insert(word2)
wordDao.deleteAll()
val allWords = wordDao.getAllWords().waitForValue()
Assert.assertTrue(allWords.isEmpty())
}
}
最後貼上solution
https://github.com/mars1120/jetpackMvvmDemo/tree/room