Back to IF3210 Pengembangan Aplikasi Piranti Bergerak

Testing Room Databases with AndroidJUnit4

Questions/Cues

  • Mengapa in-memory database lebih disukai untuk testing dibanding database biasa?
  • Apa fungsi anotasi @RunWith, @Before, @After, dan @Test?
  • Bagaimana cara membuat instance database Room untuk keperluan testing?
  • Apa yang harus diverifikasi dalam sebuah tes insert-and-retrieve?
  • Di mana file test instrumentasi Android ditempatkan dalam struktur proyek?

Reference Points

  • IF3210 Pengembangan Aplikasi Piranti Bergerak (Slides 46-51)

Mengapa Testing Database Penting

Database adalah komponen kritikal dalam banyak aplikasi Android — kesalahan dalam logika query atau definisi Entity dapat menyebabkan data pengguna hilang, korup, atau tidak terbaca. Automated testing memastikan bahwa DAO dan Entity berperilaku sesuai harapan, dan bahwa perubahan kode di masa depan tidak secara tidak sengaja merusak fungsionalitas yang sudah ada (regresi).

Untuk menguji komponen yang bergantung pada framework Android (seperti Room yang membutuhkan Context), digunakan Instrumentation Test yang berjalan pada perangkat Android fisik atau emulator, bukan pada JVM biasa di mesin pengembang.

AndroidJUnit4 dan Anotasi Testing

AndroidJUnit4 adalah test runner yang memungkinkan pengujian kode Android pada perangkat nyata atau emulator sambil tetap menggunakan sintaks JUnit 4 yang familiar. Test instrumentasi ditempatkan di direktori androidTest/ (bukan test/ yang digunakan untuk unit test murni).

Anotasi-anotasi utama yang digunakan:

  • @RunWith(AndroidJUnit4::class): Dideklarasikan di level kelas. Menginstruksikan JUnit untuk menggunakan AndroidJUnit4 sebagai test runner, yang mengelola siklus hidup test di lingkungan Android
  • @Before: Metode yang dianotasi ini dijalankan sebelum setiap metode @Test. Digunakan untuk setup — membuat instance database dan DAO
  • @After: Metode yang dianotasi ini dijalankan setelah setiap metode @Test. Digunakan untuk teardown — menutup database agar tidak ada resource leak antar test
  • @Test: Menandai sebuah metode sebagai kasus uji. Metode ini dijalankan oleh test runner dan hasilnya (pass/fail) dicatat

In-Memory Database untuk Testing

Perbedaan kunci antara setup database untuk testing dan produksi adalah penggunaan Room.inMemoryDatabaseBuilder() alih-alih Room.databaseBuilder():

  • Database biasa (databaseBuilder): Data disimpan ke file .db di storage perangkat dan persisten antar sesi
  • In-memory database (inMemoryDatabaseBuilder): Data hanya ada di RAM dan otomatis terhapus saat koneksi database ditutup

Keunggulan in-memory database untuk testing:

  • Isolasi: Setiap test mendapat database yang bersih, tidak terpengaruh oleh test lain
  • Kecepatan: Operasi RAM jauh lebih cepat daripada operasi disk
  • Tidak meninggalkan artefak: Tidak ada file .db sisa yang perlu dibersihkan setelah test selesai

Struktur Test Class

Berikut adalah struktur lengkap kelas test untuk Room DAO:

@RunWith(AndroidJUnit4::class)
class WordDaoTest {
 
    private lateinit var wordDao: WordDao
    private lateinit var db: WordRoomDatabase
 
    @Before
    fun createDb() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        db = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java)
            .allowMainThreadQueries()
            .build()
        wordDao = db.wordDao()
    }
 
    @After
    fun closeDb() {
        db.close()
    }
 
    @Test
    @Throws(Exception::class)
    fun insertAndGetWord() {
        val word = Word(word = "hello")
        wordDao.insert(word)
        val allWords = wordDao.getAlphabetizedWords().first()
        assertEquals(allWords[0].word, word.word)
    }
}

Beberapa poin penting dari kode di atas:

  • ApplicationProvider.getApplicationContext(): Menyediakan Context Android yang valid di dalam test instrumentasi tanpa perlu Activity
  • .allowMainThreadQueries(): Opsi khusus untuk testing yang memperbolehkan operasi database di main thread. Ini tidak boleh digunakan di kode produksi, hanya untuk menyederhanakan kode test
  • assertEquals: Fungsi JUnit untuk memverifikasi bahwa nilai aktual sama dengan nilai yang diharapkan. Test gagal jika nilainya berbeda

Alur Eksekusi Test

Untuk setiap metode @Test, alur eksekusinya adalah:

  1. @Before dipanggil — database in-memory baru dibuat, DAO disiapkan
  2. Metode @Test dijalankan — operasi dilakukan, assertion diverifikasi
  3. @After dipanggil — database ditutup, memori dibebaskan
  4. Siklus ini berulang untuk setiap @Test, memastikan isolasi penuh antar kasus uji

Summary

Testing database Room menggunakan AndroidJUnit4 sebagai test runner (dideklarasikan via @RunWith) karena test memerlukan context Android nyata. Setup dilakukan di metode @Before dengan membuat in-memory database menggunakan Room.inMemoryDatabaseBuilder() — yang menjamin isolasi dan kecepatan karena data hanya ada di RAM dan terhapus saat database ditutup. Teardown dilakukan di metode @After dengan menutup database. Setiap metode @Test melakukan operasi pada DAO lalu memverifikasi hasilnya dengan assertEquals. Opsi .allowMainThreadQueries() digunakan khusus untuk testing agar kode test tetap sederhana tanpa perlu coroutine setup.