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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Testing Android Architecture Components
20 mins
- Getting Started
- What Are Android Architecture Components?
- Structuring Your Tests
- Testing on Android
- Testing ViewModel and LiveData
- Verifying onChanged() Events
- Asserting LiveData Values
- Testing DAO
- Testing Room Migrations
- Creating Your Room Migration Test
- Testing Incremental Migration
- Testing All Migrations
- Where to Go From Here?
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:
- Set up
runMigrationsAndValidate
, which runs the given set of migrations on the provided database. - After the migration, the method validates the database schema to ensure that the migration result matches the expected schema.
- 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.
- Finally, you get the indexes for the columns of
author
andtext
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:
- Create the earliest version of the database.
- 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.
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.