Testing Android Architecture Components

Learn how to test the Architecture Components library included in the Android Jetpack suite released in 2017 by Google’s Android Team. By Enzo Lizama.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Creating Your Room Migration Test

Create a new test class by right-clicking on the androidTest package, select New ▸ Kotlin File/Class and name the new class RWQuoteMigrationTest. Then, add the following code to it:

@RunWith(AndroidJUnit4::class)
class RWQuoteMigrationTest {

  private lateinit var database: SupportSQLiteDatabase

  @get:Rule
  val instantTaskExecutorRule = InstantTaskExecutorRule()

  companion object {
    private const val TEST_DB = "migration-test"
  }

  @get:Rule
  val migrationTestHelper = MigrationTestHelper(
      InstrumentationRegistry.getInstrumentation(),
      RWQuotesDatabase::class.java.canonicalName,
      FrameworkSQLiteOpenHelperFactory()
  )

  // Your test goes here ...
}

In this class, you need to define a new Rule to make the testing process possible. MigrationTestHelper creates a new migration helper. It uses the Instrumentation context to load the schema — which falls back to the app resources — and the target context to create the database.

In the next section, you’ll begin to test a room migration.

Testing Incremental Migration

In the project, open data/Migrations.kt.

@VisibleForTesting
val MIGRATION_1_2 = object : Migration(1, 2) {
  override fun migrate(database: SupportSQLiteDatabase) {
    database.execSQL("ALTER TABLE rwquotes ADD COLUMN 'stars' INTEGER NOT NULL DEFAULT 0")
  }
}

The above migration represents the difference between the first and second version of the same Entity for the database. For this example, the entity is Quote.

It represents that the second version of the table rwquotes has stars as a new attribute. So, to avoid incompatibilities between versions, you have to do a migration.

Back in RWQuoteMigrationTest. Add the following test:

  @Test
  fun migrate1to2() {
    // 1
    database = migrationTestHelper.createDatabase(TEST_DB, 1).apply {

      execSQL(
          """
                INSERT INTO rwquotes VALUES (10, 'Hello', 'Shakespeare', '12/12/21')
                """.trimIndent()
      )
      close()
    }

    // 2
    database = migrationTestHelper.runMigrationsAndValidate(TEST_DB, 2, true, MIGRATION_1_2)

    // 3
    val resultCursor = database.query("SELECT * FROM rwquotes")
    // Make sure you can find the age column, and assert it's equal to the default.
    // You can also validate the name is the one you inserted.
    assertTrue(resultCursor.moveToFirst())

    // 4
    val authorColumnIndex = resultCursor.getColumnIndex("author")
    val textColumnIndex = resultCursor.getColumnIndex("text")

    val authorFromDatabase = resultCursor.getString(authorColumnIndex)
    val textFromDatabase = resultCursor.getString(textColumnIndex)

    assertEquals("Shakespeare", authorFromDatabase)
    assertEquals("Hello", textFromDatabase)
  }

To verify the migration is successful, you have to test it. To achieve this, you:

  1. Set up runMigrationsAndValidate, which runs the given set of migrations on the provided database.
  2. After the migration, the method validates the database schema to ensure that the migration result matches the expected schema.
  3. When the validation succeeds, you ensure that inserted values are the expected ones, making a query to retrieve all the values for the database located in memory.
  4. Finally, you get the indexes for the columns of author and text and check if the values are what you expected after the assertion.

Good job. That’s one migration of your database tests. In the next section, you’ll learn how to test multiple migrations.

Testing All Migrations

In the step above, you tested the migration from two different versions, but in the real world, you’d have many more versions to test. Therefore, it’s highly recommended to include a test that covers all the migrations defined in your database. This ensures there are no problems between a newer database instance and an older one that follows the migrations.

Add the following test to the bottom of RWQuoteMigrationTest.

  private val ALL_MIGRATIONS = arrayOf(MIGRATION_1_2, MIGRATION_2_3)

  @Test
  @Throws(IOException::class)
  fun migrateAll() {
    // 1
    migrationTestHelper.createDatabase(TEST_DB, 2).apply {
      close()
    }
    // 2
    Room.databaseBuilder(
        InstrumentationRegistry.getInstrumentation().targetContext,
        RWQuotesDatabase::class.java,
        TEST_DB
    ).addMigrations(*ALL_MIGRATIONS).build().apply {
      openHelper.writableDatabase
      close()
    }
  }

To test all the migrations for the local database, follow these steps:

  1. Create the earliest version of the database.
  2. Open the latest version of the database. Room will validate the schema once all migrations execute.

So now you can run your tests and expect a successful result.

Test results showing a successful migration

All tests are green. Looking good!

Where to Go From Here?

You’ve just learnt how to unit test commonly used architecture components in Android.

You can download the completed project files by using the Download Materials button at the top or bottom of the tutorial.

If you’re more curious about Android topics, take a look into the Android developer docs for more info.

We hope you enjoyed this tutorial. If you have any questions, please join the discussion below.