One Page

Room Testing: Off Device

July 29, 2019

The official instructions for testing room, of which you cna see a complete example here, suggest that you should always test on a device.

This is true for many reasons but what if you’re experiementing with Room for the first time and just wanted to get a hang for it?

Or even, trying to see if it’ll work the way you expect in your app but don’t want to wait for all that time it’d take to keep re-running your tests on device? You could run it against a local SQLite library on your computer, but then the official documentation warns:

Note: Even though this setup allows your tests to run very quickly, it isn’t recommended because the version of SQLite running on your device—and your users’ devices—might not match the version on your host machine

There are two techniques when used together, should completely mitigate this problem.

Mitigation 1

However we can get around this a bit better by using Robolectric to simulate the sqlite support instead.

This requires a few changes to your imports because now you’re running a hybrid of unit and integration tests and a few libraries that are usually only imported for androidTest will now be required in test.

What we get in exchange for this is significantly faster tests that don’t require an external device.

So finally, your test code looks like this and runs in 6 seconds at the most:

@LargeTest
@RunWith(RobolectricTestRunner::class)
class ImageDataDaoTest {

    @get:Rule
    val rule = InstantTaskExecutorRule()

    private lateinit var imageDataDao: ImageDataDao
    private lateinit var db: ImageDatabase

    @Before
    fun createDb() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        db = Room.inMemoryDatabaseBuilder(context, ImageDatabase::class.java).allowMainThreadQueries().build()
        imageDataDao = db.imageDataDao()
    }

    @After
    @Throws(IOException::class)
    fun closeDb() = db.close()

    @Test
    fun writeImageAndRead() {
        val sampleData = ImageData("what", "Hello", "www.imagedb.com")

        imageDataDao.insert(sampleData).blockingGet()

        val result: List<ImageData>? = LiveDataTestUtil.getValue(imageDataDao.getAllImageData())
        assertEquals(result?.get(0), sampleData)

    }
}

Mitigation 2

Robolectric, after all, is a re-implementation of android framework code and may not match the actual code’s behaviour in subtle ways. Kaushik mentions as much in a fragmentedcast episode.

The quick tests still help us move fast in one area, and are valuable even though edge cases may exist.

To catch the edge cases, it’s good to have a fully instrumented test with the real database (albeit mocked server responses), checked with Espresso.

With that you can rapidly prototype with robolectric and run final checks on a real device with Espresso to check if everything is working the way you expect at one higher level of abstraction (and order of magnitude of slowness).

For the full code for this example see this commit. Happy coding!

Have suggestions for a topic I should cover? Send me a dm at @AniketSMK or email me at hello@[firstname][lastname].com


Aniket Kadam, author

Written by Aniket Kadam - building useful things. Follow me on Twitter