Testing Room Database in AndroidTest

36 Views Asked by At

hay there hope you doing good so I've been trying to implement a test for my room data base in AndroidTest directory

here is the test i have implemented :


@RunWith(AndroidJUnit4::class)
@SmallTest
class ShoppingDaoTest {

    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    private lateinit var database: ShoppingItemDatabase
    private lateinit var dao: ShoppingDao

    @Before
    fun setup() {
        database = Room.inMemoryDatabaseBuilder(
            ApplicationProvider.getApplicationContext(),
            ShoppingItemDatabase::class.java
        ).allowMainThreadQueries().build()
        dao = database.shoppingDao()
    }

    @After
    fun teardown() {
        database.close()
    }


    @Test
    fun insertShoppingItem() = runTest {
        val shoppingItem = ShoppingItem("name", 1, 1f, "url", id = 1)
        dao.insertShoppingItem(shoppingItem)

        val allShoppingItems = dao.observeAllShoppingItems().getOrAwaitValue()

        assertThat(allShoppingItems).contains(shoppingItem)
    }
}

my Dao :

@Dao
interface ShoppingDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertShoppingItem(shoppingItem: ShoppingItem)
    @Delete
    suspend fun deleteShoppingItem(shoppingItem: ShoppingItem)
    @Update
    suspend fun updateShoppingItem(shoppingItem: ShoppingItem)

    @Query("SELECT * FROM `shopping-items`")
    fun observeAllShoppingItems(): LiveData<List<ShoppingItem>>
    @Query("SELECT SUM(price * amount) FROM `shopping-items`")
    fun observeTotalPrice(): LiveData<Float>
}

my Database Class :

@Database(entities = [ShoppingItem::class], version = 1, exportSchema = false)

abstract class ShoppingItemDatabase:RoomDatabase() {
    abstract fun shoppingDao():ShoppingDao
}

the Entity:

@Entity(tableName = "shopping-items")
data class ShoppingItem(
    var name: String,
    var amount: Int,
    var price: Float,
    var imageUrl: String,
    @PrimaryKey(autoGenerate = true)
    val id: Int? = null
)

getOrAwaitValue function :

@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T) {
            data = o
            latch.countDown()
            [email protected](this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

When i run this code the result is an error

java.lang.IllegalStateException:Cannot invoke observeForever on a background thread

the full log if needed :

12-15 13:27:40.065 12955 12978 I TestRunner: started: observeTotalPriceSum(com.example.myshoppinglist.data.local.ShoppingDaoTest)
12-15 13:27:40.213 12955 12978 E TestRunner: failed: observeTotalPriceSum(com.example.myshoppinglist.data.local.ShoppingDaoTest)
12-15 13:27:40.213 12955 12978 E TestRunner: ----- begin exception -----
12-15 13:27:40.215 12955 12978 E TestRunner: java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:502)
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.lifecycle.LiveData.observeForever(LiveData.java:224)
12-15 13:27:40.215 12955 12978 E TestRunner:    at com.example.myshoppinglist.LiveDataUtilAndroidTestKt.getOrAwaitValue(LiveDataUtilAndroidTest.kt:47)
12-15 13:27:40.215 12955 12978 E TestRunner:    at com.example.myshoppinglist.LiveDataUtilAndroidTestKt.getOrAwaitValue$default(LiveDataUtilAndroidTest.kt:33)
12-15 13:27:40.215 12955 12978 E TestRunner:    at com.example.myshoppinglist.data.local.ShoppingDaoTest$observeTotalPriceSum$1.invokeSuspend(ShoppingDaoTest.kt:76)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:28)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTaskUnless$kotlinx_coroutines_test(TestCoroutineScheduler.kt:103)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$workRunner$1.invokeSuspend(TestBuilders.kt:320)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source:1)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersJvmKt.createTestResult(TestBuildersJvm.kt:13)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:308)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source:1)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:166)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source:1)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0$default(TestBuilders.kt:158)
12-15 13:27:40.215 12955 12978 E TestRunner:    at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0$default(Unknown Source:1)
12-15 13:27:40.215 12955 12978 E TestRunner:    at com.example.myshoppinglist.data.local.ShoppingDaoTest.observeTotalPriceSum(ShoppingDaoTest.kt:68)
12-15 13:27:40.215 12955 12978 E TestRunner:    at java.lang.reflect.Method.invoke(Native Method)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:162)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.Suite.runChild(Suite.java:128)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.Suite.runChild(Suite.java:27)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
12-15 13:27:40.215 12955 12978 E TestRunner:    at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:67)
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:58)
12-15 13:27:40.215 12955 12978 E TestRunner:    at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:446)
12-15 13:27:40.215 12955 12978 E TestRunner:    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2402)
12-15 13:27:40.215 12955 12978 E TestRunner: ----- end exception -----
12-15 13:27:40.220 12955 12978 I TestRunner: finished: observeTotalPriceSum(com.example.myshoppinglist.data.local.ShoppingDaoTest)

java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:502)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:224)
at com.example.myshoppinglist.LiveDataUtilAndroidTestKt.getOrAwaitValue(LiveDataUtilAndroidTest.kt:47)
at com.example.myshoppinglist.LiveDataUtilAndroidTestKt.getOrAwaitValue$default(LiveDataUtilAndroidTest.kt:33)
at com.example.myshoppinglist.data.local.ShoppingDaoTest$observeTotalPriceSum$1.invokeSuspend(ShoppingDaoTest.kt:76)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:28)
at kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTaskUnless$kotlinx_coroutines_test(TestCoroutineScheduler.kt:103)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$workRunner$1.invokeSuspend(TestBuilders.kt:320)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:280)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source:1)
at kotlinx.coroutines.test.TestBuildersJvmKt.createTestResult(TestBuildersJvm.kt:13)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:308)
at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source:1)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:166)
at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source:1)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0$default(TestBuilders.kt:158)
at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0$default(Unknown Source:1)
at com.example.myshoppinglist.data.local.ShoppingDaoTest.observeTotalPriceSum(ShoppingDaoTest.kt:68)

and the dependency I'm Using in case you need it :

// Room
    val room_version = "2.6.1"

    implementation("androidx.room:room-runtime:$room_version")
    kapt("androidx.room:room-compiler:$room_version")
    implementation("androidx.room:room-ktx:$room_version")
    testImplementation("androidx.room:room-testing:$room_version")

    // coroutines
    val coroutines_version = "1.7.1"
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version")
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")
    androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")

    // Google Truth Library
    androidTestImplementation("com.google.truth:truth:1.1.4")
    testImplementation("com.google.truth:truth:1.1.4")

    // test Imp
    testImplementation("android.arch.core:core-testing:1.1.1")
    androidTestImplementation("android.arch.core:core-testing:1.1.1")

I've tried fixing it by adding the following line :

var instantTaskExecutorRule = InstantTaskExecutorRule()

if i remove it the error changes to this : java.lang.IllegalStateException: This job has not completed

is there something I'm missing ? how can i fix this problem ?

0

There are 0 best solutions below