lateinit 属性 colorPalettesDB 尚未初始化 - 将第二个 Room 数据库添加到应用程序会导致所有 Espresso 测试失败

lateinit property colorPalettesDB has not been initialized - adding second Room database to application causes all Espresso tests to fail

我正在为 Android 创建一个像素艺术编辑器应用程序,我有两个数据库,一个存储用户的创作,另一个存储用户的调色板。

这是调色板数据库的代码:

package com.realtomjoney.pyxlmoose.database

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.realtomjoney.pyxlmoose.converters.JsonConverter
import com.realtomjoney.pyxlmoose.dao.ColorPalettesDao
import com.realtomjoney.pyxlmoose.models.ColorPalette
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.concurrent.Executors

@Database(entities = [ColorPalette::class], version = 3)
abstract class ColorPalettesDatabase: RoomDatabase() {
    abstract fun colorPalettesDao(): ColorPalettesDao

    companion object {
        private var instance: ColorPalettesDatabase? = null
        fun getDatabase(context: Context): ColorPalettesDatabase {
            if (instance == null) {
                synchronized(ColorPalettesDatabase::class) {
                    if (instance == null) instance = Room.databaseBuilder(context.applicationContext, ColorPalettesDatabase::class.java, AppData.colorPalettesDBFileName).fallbackToDestructiveMigration().addCallback(object : RoomDatabase.Callback() {
                        override fun onCreate(db: SupportSQLiteDatabase) {
                            super.onCreate(db)
                            Executors.newSingleThreadExecutor().execute {
                                CoroutineScope(Dispatchers.IO).launch {
                                    instance?.colorPalettesDao()?.insertColorPalette(ColorPalette("Default color palette", JsonConverter.convertListOfIntToJsonString(ColorDatabase.toList()), true))
                                }
                            }
                        }
                    }).allowMainThreadQueries().build()
                }
            }
            return instance!!
        }
    }
}

首次创建 Room 数据库时,会添加 'Default' 调色板。

道:

@Dao
interface ColorPalettesDao {
    @Insert
    suspend fun insertColorPalette(colorPalette: ColorPalette)

    @Query("SELECT * FROM ColorPalette")
    fun getAllColorPalettes(): LiveData<List<ColorPalette>>

    @Query("DELETE FROM ColorPalette WHERE objId=:colorPaletteId")
    fun deleteColorPalette(colorPaletteId: Int)

    @Query("UPDATE ColorPalette SET item_color_palette_color_data=:colorData WHERE objId=:id_t")
    fun updateColorPaletteColorData(colorData: String, id_t: Int)
}

应用程序数据:

class AppData {
    companion object {
        var pixelArtDBFileName = "pixel_art_db"
        lateinit var pixelArtDB: PixelArtDatabase

        var colorPalettesDBFileName = "color_palettes_db"
        lateinit var colorPalettesDB: ColorPalettesDatabase
    }
}

数据像这样加载到 RecyclerView 中:

fun CanvasActivity.extendedSetUpRecyclerView() {
    val layoutManager = GridLayoutManager(this, 1)
    layoutManager.orientation = LinearLayoutManager.HORIZONTAL
    binding.activityCanvasColorPickerRecyclerView.layoutManager = layoutManager

    AppData.colorPalettesDB.colorPalettesDao().getAllColorPalettes().observe(this) {
        val toShow = if (fromDB != null) fromDB else it.first()
        binding.activityCanvasColorPickerRecyclerView.adapter = ColorPickerAdapter(toShow!!, this)
    }
}

以下是我的一些 Espresso 测试:

@LargeTest
@RunWith(AndroidJUnit4ClassRunner::class)
class CanvasActivityTest {
    @get:Rule
    var activityTestRule = ActivityScenarioRule(CanvasActivity::class.java)

    @Test fun uitest_activityCanvasRootLayout_childViews_doNotExist() {
        for (id in EspressoUtilities.getActivityCanvasRootLayoutChildElementIds()) onView(withId(id)).check(matches(isDisplayed()))
    }

    @Test fun uitest_fullscreenMenuItem_isDisplayed() {
        onView(withId(R.id.fullscreen)).check(matches(isDisplayed()))
    }

    @Test fun uitest_undoMenuItem_isDisplayed() {
        onView(withId(R.id.undo)).check(matches(isDisplayed()))
    }

    @Test fun uitest_zoomIn_isDisplayed() {
        onView(withId(R.id.zoom_in)).check(matches(isDisplayed()))
    }

    @Test fun uitest_zoomOut_isDisplayed() {
        onView(withId(R.id.zoom_out)).check(matches(isDisplayed()))
    }

    @Test fun uitest_saveProject_isDisplayed() {
        onView(withId(R.id.save_project)).check(matches(isDisplayed()))
    }
}

运行 Espresso 测试导致以下异常:

kotlin.UninitializedPropertyAccessException: lateinit property colorPalettesDB has not been initialized
at com.realtomjoney.pyxlmoose.database.AppData$Companion.getColorPalettesDB(AppData.kt:9)
at com.realtomjoney.pyxlmoose.activities.canvas.CanvasActivity_setUpRecyclerViewKt.extendedSetUpRecyclerView(CanvasActivity+setUpRecyclerView.kt:15)
at com.realtomjoney.pyxlmoose.activities.canvas.CanvasActivity.setUpRecyclerView(CanvasActivity.kt:45)
at com.realtomjoney.pyxlmoose.activities.canvas.CanvasActivity_onCreateKt.extendedOnCreate(CanvasActivity+onCreate.kt:13)
at com.realtomjoney.pyxlmoose.activities.canvas.CanvasActivity.onCreate(CanvasActivity.kt:19)
at android.app.Activity.performCreate(Activity.java:8051)
at android.app.Activity.performCreate(Activity.java:8031)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1329)
at androidx.test.runner.MonitoringInstrumentation.callActivityOnCreate(MonitoringInstrumentation.java:769)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3612)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3796)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2214)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

这很奇怪,因为 colorPalettesDB 应该已经在这个阶段初始化了。存储用户创建的其他数据库没有引起任何问题,但由于某种原因 ColorPalettesDatabase 是导致这些异常的数据库。在 onCreate 和 Espresso 代码中第二次初始化它并没有解决问题,所以我很困惑如何解决这个问题,因为我不确定错误的来源是什么。


正在添加

  Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(),ColorPalettesDatabase::class.java).build()

...无法解决问题。


这里是数据库初始化的地方:

fun MainActivity.initializeRoomDatabases() {
    AppData.pixelArtDB = PixelArtDatabase.getDatabase(this)
    AppData.colorPalettesDB = ColorPalettesDatabase.getDatabase(this)
}
public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setBindings()
        setOnClickListeners()
        setTitle()
        initializeRoomDatabases()
    }

请在您的测试中使用下面的代码。

Room.inMemoryDatabaseBuilder(context, TestDatabase::class.java).build()

更多信息:Test and debug your database

更新:

错误提示您必须以某种方式初始化 colorPalettesDB。也许你应该在 DI 中定义它。

更新二:

我想也许 CanvasActivity.extendedSetUpRecyclerView() 在 Activity 中的 MainActivity.initializeRoomDatabases() 之前调用了方法,所以 AppData.colorPalettesDB 没有被初始化。