【问题标题】:lateinit property colorPalettesDB has not been initialized - adding second Room database to application causes all Espresso tests to faillateinit 属性 colorPalettesDB 尚未初始化 - 将第二个 Room 数据库添加到应用程序会导致所有 Espresso 测试失败
【发布时间】:2022-01-24 19:56:52
【问题描述】:

我正在为 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 数据库时,会添加“默认”调色板。

道:

@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()
    }

【问题讨论】:

  • 那么您的代码在哪里初始化该变量?为什么您的代码根本不调用 ColorPalettesDatabase.getInstance(context) 而不是使用 lateinit 变量?
  • @ianhanniballake 因为 AppData 只是一个 Object 它无法访问应用程序上下文...这就是为什么需要从 Activity 调用它的原因。如果我做的不对,请告诉我,代码的大部分结构都是基于 Udemy 课程。
  • 也许你在 setBindings() 中使用 colorPalettesDB,因为在你的堆栈跟踪中有一个地方说在这部分代码尝试使用 colorPalettesDB:CanvasActivity_setUpRecyclerViewKt.extendedSetUpRecyclerView
  • @MohammadRezaKhahani 对不起,我不关注。

标签: android kotlin android-room android-espresso


【解决方案1】:

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

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

更多信息:Test and debug your database

更新:

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

更新 2:

我认为可能是在您的 Activity 中 MainActivity.initializeRoomDatabases() 之前调用的 CanvasActivity.extendedSetUpRecyclerView() 方法,因此 AppData.colorPalettesDB 未初始化。

【讨论】:

  • 我没有测试我的数据库,这是一个 Espresso 测试。当我的代码中只有一个 Room 数据库时,我也从来没有添加过这个代码,你能解释一下吗?
  • 添加 - 在我的 @Before 函数中添加此代码并不能解决此问题。
  • 您的第二点似乎没有意义......当您需要通过MainActivity导航到CanvasActivity时,如何在MainActivity中的方法之前调用CanvasActivity中的方法。如果您的意思是单元测试,那么是的,也许这就是出现问题的原因,因为它只在 CanvasActivity 中直接启动 Espresso 测试,而不通过 MainActivity。奇怪的是,即使我在 CanvasActivity 中初始化了数据库,我仍然在 Espresso 中遇到同样的错误。在 Discord 上联系我:Tom Joney#5406 让我们多谈谈
  • 是的,如果您直接转到CanvasActivity 而不先创建MainActivity,则不会初始化该字段。如果您将该初始化代码移动到CanvasActivity,它必须在您调用extendedSetupRecyclerView 之前运行。 lateinit 只是您向编译器保证您将在该字段上设置一个值在您尝试从中读取之前 - 如果您仍然遇到同样的崩溃,那么您还没有这样做
猜你喜欢
  • 2017-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-31
  • 2021-06-11
  • 2017-12-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多