Testing Room
Once you have a RoomDatabase
and its associated DAO(s) and entities set up, you should start testing it.
The good news is that testing Room is not dramatically different than is testing anything else in Android. Room has a few characteristics that make it a bit easier than some things to test, as it turns out.
Writing Instrumented Tests
On the whole, writing instrumented tests for Room — where the tests run on an Android device or emulator — is unremarkable. You get an instance of your RoomDatabase
subclass and exercise it from there.
So, for example, here is an instrumented test case class to exercise the NoteDatabase
:
package com.commonsware.room.notes
import androidx.room.Room
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.natpryce.hamkrest.assertion.assertThat
import com.natpryce.hamkrest.equalTo
import com.natpryce.hamkrest.hasSize
import com.natpryce.hamkrest.isEmpty
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
@RunWith(AndroidJUnit4::class)
class NoteStoreTest {
private val db = Room.inMemoryDatabaseBuilder(
InstrumentationRegistry.getInstrumentation().targetContext,
NoteDatabase::class.java
)
.build()
private val underTest = db.notes()
@Test
fun insertAndDelete() {
assertThat(underTest.loadAll(), isEmpty)
val entity = NoteEntity(
id = UUID.randomUUID().toString(),
title = "This is a title",
text = "This is some text",
version = 1
)
underTest.insert(entity)
underTest.loadAll().let {
assertThat(it, hasSize(equalTo(1)))
assertThat(it[0], equalTo(entity))
}
underTest.delete(entity)
assertThat(underTest.loadAll(), isEmpty)
}
@Test
fun update() {
val entity = NoteEntity(
id = UUID.randomUUID().toString(),
title = "This is a title",
text = "This is some text",
version = 1
)
underTest.insert(entity)
val updated = entity.copy(title = "This is new", text = "So is this")
underTest.update(updated)
underTest.loadAll().let {
assertThat(it, hasSize(equalTo(1)))
assertThat(it[0], equalTo(updated))
}
}
}
Using In-Memory Databases
When testing a database, though, one of the challenges is in making those tests “hermetic”, or self-contained. One test method should not depend upon another test method, and one test method should not affect the results of another test method accidentally. This means that we want to start with a known starting point before each test, and we have to consider how to do that.
One approach — the one taken in the above NoteStoreTest
class — is to use an in-memory database. The db
property is initialized using Room.inMemoryDatabaseBuilder()
, so we get our fast, disposable in-memory database. For a context, we use InstrumentationRegistry.getInstrumentation().targetContext
, a Context
for the code being tested. We then set up underTest
to be the object that we are testing: the NoteStore
and its functions.
There are two key advantages for using an in-memory database for instrumented testing:
- It is intrinsically self-contained. Once the
NoteDatabase
is closed (or garbage-collected), its memory is released, and if separate tests use separateNoteDatabase
instances, one will not affect the other. - Reading and writing to and from memory is much faster than is reading and writing to and from disk, so the tests run much faster.
On the other hand, this means that the instrumented tests are useless for performance testing, as (presumably) your production app will actually store its database on disk. You could use Gradle command-line switches, custom build types and buildConfigField
, or other means to decide when tests are run whether they should use memory or disk.
The Test Functions
Our test functions do things like:
- Creating
NoteEntity
instances, using a UUID for theid
- Calling
insert()
,update()
, anddelete()
to manipulate the table contents - Calling
loadAll()
to retrieve what is in the table
And, along the way, we use Hamkrest matchers to confirm that everything is working as we expect.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.