Issue
I am creating a pixel art editor app for Android, and I have two databases, one that stores the users' creations, and another that stores the users' color palettes.
Here's the code for the color palettes database:
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!!
}
}
}
When the Room database is first created, the 'Default' color palette is added.
Dao:
@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)
}
AppData:
class AppData {
companion object {
var pixelArtDBFileName = "pixel_art_db"
lateinit var pixelArtDB: PixelArtDatabase
var colorPalettesDBFileName = "color_palettes_db"
lateinit var colorPalettesDB: ColorPalettesDatabase
}
}
The data is loaded into the RecyclerView like so:
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)
}
}
Here are some of my Espresso tests:
@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()))
}
}
Running the Espresso test causes the following exception:
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)
This is strange because colorPalettesDB
should've already been intialized at this stage. The other database which stores the users' creations is not causing any issues, but for some reason the ColorPalettesDatabase
is the database causing these exceptions. Initializing it a second time in onCreate
and in the Espresso code does not fix the problem, so I'm confused how I would fix this, as I'm unsure what the source of the error is.
Adding
Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(),ColorPalettesDatabase::class.java).build()
...does not fix the issue.
Here is where the database gets initialized:
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()
}
Solution
Please use the code below in your tests.
Room.inMemoryDatabaseBuilder(context, TestDatabase::class.java).build()
more info: Test and debug your database
Update:
The error said that you have to initialize colorPalettesDB somehow. Maybe you should define it in the DI.
Update 2:
I think maybe the method CanvasActivity.extendedSetUpRecyclerView() called before MainActivity.initializeRoomDatabases() in your Activity, so the AppData.colorPalettesDB is not initialized.
Answered By - Mohammad Reza Khahani
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.