1. Sebelum memulai
Anda telah mempelajari codelab sebelumnya tentang cara menggunakan library persistensi Room, yaitu lapisan abstraksi di atas database SQLite untuk menyimpan data aplikasi. Dalam codelab ini, Anda akan menambahkan lebih banyak fitur ke aplikasi Inventory dan mempelajari cara membaca, menampilkan, memperbarui, dan menghapus data dari database SQLite menggunakan Room. Anda akan menggunakan LazyColumn untuk menampilkan data dari database dan memperbarui data secara otomatis saat data pokok dalam database berubah.
Prasyarat
- Kemampuan membuat dan berinteraksi dengan database SQLite menggunakan library Room.
- Kemampuan membuat entity, DAO, dan class database.
- Kemampuan menggunakan objek akses data (DAO) untuk memetakan fungsi Kotlin ke kueri SQL.
- Kemampuan untuk menampilkan item daftar di
LazyColumn. - Penyelesaian codelab sebelumnya di unit ini, Mempertahankan data dengan Room.
Yang akan Anda pelajari
- Cara membaca dan menampilkan entity dari database SQLite.
- Cara memperbarui dan menghapus entity dari database SQLite menggunakan library Room.
Yang akan Anda build
- Aplikasi Inventory yang menampilkan daftar item inventaris dan dapat memperbarui, mengedit, serta menghapus item dari database aplikasi menggunakan Room.
Yang akan Anda butuhkan
- Komputer dengan Android Studio
2. Ringkasan aplikasi awal
Codelab ini menggunakan kode solusi aplikasi Inventory dari codelab sebelumnya, Mempertahankan data dengan Room sebagai kode awal. Aplikasi awal sudah menyimpan data dengan library persistensi Room. Pengguna dapat menggunakan layar Add Item untuk menambahkan data ke database aplikasi.
|
|
Dalam codelab ini, Anda akan memperluas aplikasi untuk membaca dan menampilkan data, serta memperbarui dan menghapus entity di database menggunakan library Room.
Mendownload kode awal untuk codelab ini
Untuk memulai, download kode awal:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-inventory-app.git $ cd basic-android-kotlin-compose-training-inventory-app $ git checkout room
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode awal untuk codelab ini, lihat kode tersebut di GitHub.
3. Mengupdate status UI
Dalam tugas ini, Anda akan menambahkan LazyColumn ke aplikasi untuk menampilkan data yang tersimpan dalam database.

Panduan fungsi composable HomeScreen
- Buka file
ui/home/HomeScreen.ktdan lihat composableHomeScreen().
@Composable
fun HomeScreen(
navigateToItemEntry: () -> Unit,
navigateToItemUpdate: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(
topBar = {
// Top app with app title
},
floatingActionButton = {
FloatingActionButton(
// onClick details
) {
Icon(
// Icon details
)
}
},
) { innerPadding ->
// Display List header and List of Items
HomeBody(
itemList = listOf(), // Empty list is being passed in for itemList
onItemClick = navigateToItemUpdate,
modifier = modifier.padding(innerPadding)
.fillMaxSize()
)
}
Fungsi composable ini menampilkan item berikut:
- Panel aplikasi atas dengan judul aplikasi
- Tombol tindakan mengambang (FAB) untuk penambahan item baru ke inventaris

- Fungsi composable
HomeBody()
Fungsi composable HomeBody() menampilkan item inventaris berdasarkan daftar yang diteruskan. Sebagai bagian dari penerapan kode awal, daftar kosong (listOf()) diteruskan ke fungsi composable HomeBody(). Untuk meneruskan daftar inventaris ke composable ini, Anda harus mengambil data inventaris dari repositori dan meneruskannya ke HomeViewModel.
Memberikan status UI di HomeViewModel
Saat menambahkan metode ke ItemDao untuk mendapatkan item- getItem() dan getAllItems()- Anda menentukan Flow sebagai jenis nilai yang ditampilkan. Ingat kembali bahwa Flow mewakili aliran data generik. Dengan menampilkan Flow, Anda hanya perlu memanggil metode dari DAO secara eksplisit satu kali untuk siklus proses tertentu. Room menangani update pada data pokok secara asinkron.
Mendapatkan data dari flow disebut mengumpulkan dari flow. Saat mengumpulkan dari flow di lapisan UI, ada beberapa hal yang perlu dipertimbangkan.
- Peristiwa siklus proses seperti perubahan konfigurasi, misalnya memutar perangkat, menyebabkan aktivitas dibuat ulang. Ini menyebabkan rekomposisi dan pengumpulan dari
FlowAnda diulang kembali. - Anda ingin nilai di-cache sebagai status sehingga data yang ada tidak hilang di antara peristiwa siklus proses.
- Flow harus dibatalkan jika tidak ada observer yang tersisa, seperti setelah siklus proses composable berakhir.
Cara yang direkomendasikan untuk mengekspos Flow dari ViewModel adalah dengan StateFlow. Penggunaan StateFlow memungkinkan data disimpan dan diamati, terlepas dari siklus proses UI. Untuk mengonversi Flow menjadi StateFlow, Anda menggunakan operator stateIn.
Operator stateIn memiliki tiga parameter yang dijelaskan di bawah:
scope-viewModelScopemenentukan siklus prosesStateFlow. JikaviewModelScopedibatalkan,StateFlowjuga akan dibatalkan.started- Pipeline hanya boleh aktif jika UI terlihat.SharingStarted.WhileSubscribed()digunakan untuk melakukannya. Untuk mengonfigurasi penundaan (dalam milidetik) antara hilangnya pelanggan terakhir dan penghentian coroutine berbagi, teruskanTIMEOUT_MILLISke metodeSharingStarted.WhileSubscribed().initialValue- Menetapkan nilai awal flow status keHomeUiState().
Setelah mengonversi Flow menjadi StateFlow, Anda dapat mengumpulkannya menggunakan metode collectAsState(), yang mengonversi datanya menjadi State dari jenis yang sama.
Pada langkah ini, Anda akan mengambil semua item dalam database Room sebagai API StateFlow yang dapat diamati untuk status UI. Saat data Inventaris Room berubah, UI akan otomatis diperbarui.
- Buka file
ui/home/HomeViewModel.kt, yang berisi konstantaTIMEOUT_MILLISdan class dataHomeUiStatedengan daftar item sebagai parameter konstruktor.
// No need to copy over, this code is part of starter code
class HomeViewModel : ViewModel() {
companion object {
private const val TIMEOUT_MILLIS = 5_000L
}
}
data class HomeUiState(val itemList: List<Item> = listOf())
- Di dalam class
HomeViewModel, deklarasikanvalyang disebuthomeUiStatedari jenisStateFlow<HomeUiState>. Anda akan segera mengatasi error inisialisasi.
val homeUiState: StateFlow<HomeUiState>
- Panggil
getAllItemsStream()diitemsRepositorydan tetapkan kehomeUiStateyang baru saja Anda deklarasikan.
val homeUiState: StateFlow<HomeUiState> =
itemsRepository.getAllItemsStream()
Anda sekarang mendapatkan error - Referensi yang belum terselesaikan: itemsRepository. Untuk mengatasi error Referensi yang belum terselesaikan, Anda harus meneruskan objek ItemsRepository ke HomeViewModel.
- Tambahkan parameter konstruktor jenis
ItemsRepositoryke classHomeViewModel.
import com.example.inventory.data.ItemsRepository
class HomeViewModel(itemsRepository: ItemsRepository): ViewModel() {
- Di file
ui/AppViewModelProvider.kt, di penginisialisasiHomeViewModel, teruskan objekItemsRepositoryseperti yang ditunjukkan.
initializer {
HomeViewModel(inventoryApplication().container.itemsRepository)
}
- Kembali ke file
HomeViewModel.kt. Perhatikan error ketidakcocokan jenis. Untuk mengatasi hal ini, tambahkan peta transformasi seperti yang ditunjukkan di bawah.
val homeUiState: StateFlow<HomeUiState> =
itemsRepository.getAllItemsStream().map { HomeUiState(it) }
Android Studio masih menampilkan error ketidakcocokan jenis. Error ini terjadi karena homeUiState adalah jenis StateFlow dan getAllItemsStream() menampilkan Flow.
- Gunakan operator
stateInuntuk mengonversiFlowmenjadiStateFlow.StateFlowadalah API yang dapat diamati untuk status UI, yang memungkinkan UI mengupdate dirinya sendiri.
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
val homeUiState: StateFlow<HomeUiState> =
itemsRepository.getAllItemsStream().map { HomeUiState(it) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
initialValue = HomeUiState()
)
- Build aplikasi untuk memastikan tidak ada error dalam kode. Tidak akan ada perubahan visual.
4. Menampilkan data Inventaris
Dalam tugas ini, Anda mengumpulkan dan mengupdate status UI di HomeScreen.
- Di file
HomeScreen.kt, pada fungsi composableHomeScreen, tambahkan parameter fungsi baru dari jenisHomeViewModeldan lakukan inisialisasi.
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.inventory.ui.AppViewModelProvider
@Composable
fun HomeScreen(
navigateToItemEntry: () -> Unit,
navigateToItemUpdate: (Int) -> Unit,
modifier: Modifier = Modifier,
viewModel: HomeViewModel = viewModel(factory = AppViewModelProvider.Factory)
)
- Dalam fungsi composable
HomeScreen, tambahkanvalyang disebuthomeUiStateuntuk mengumpulkan status UI dariHomeViewModel. Anda menggunakancollectAsState(), yang mengumpulkan nilai dariStateFlowdan mewakili nilai terbarunya melaluiState.
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
val homeUiState by viewModel.homeUiState.collectAsState()
- Update panggilan fungsi
HomeBody()dan teruskanhomeUiState.itemListke parameteritemList.
HomeBody(
itemList = homeUiState.itemList,
onItemClick = navigateToItemUpdate,
modifier = modifier.padding(innerPadding)
)
- Jalankan aplikasi. Perhatikan bahwa daftar inventaris ditampilkan jika Anda menyimpan item di database aplikasi. Jika daftar kosong, tambahkan beberapa item inventaris ke database aplikasi.

5. Menguji database Anda
Codelab sebelumnya membahas pentingnya menguji kode Anda. Dalam tugas ini, Anda akan menambahkan beberapa pengujian unit untuk menguji kueri DAO, lalu menambahkan lebih banyak pengujian seiring progres Anda dalam codelab.
Metode yang direkomendasikan untuk menguji penerapan database adalah menulis pengujian JUnit yang berjalan di perangkat Android. Pengujian ini tidak memerlukan pembuatan aktivitas sehingga akan lebih cepat dijalankan daripada pengujian UI.
- Dalam file
build.gradle.kts (Module :app), perhatikan dependensi berikut untuk Espresso dan JUnit.
// Testing
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
- Beralihlah ke tampilan Project, lalu klik kanan pada src > New > Directory untuk membuat set sumber pengujian bagi pengujian Anda.

- Pilih androidTest/kotlin dari pop-up New Directory.

- Buat class Kotlin bernama
ItemDaoTest.kt. - Anotasikan class
ItemDaoTestdengan@RunWith(AndroidJUnit4::class). Class Anda sekarang terlihat seperti kode contoh berikut:
package com.example.inventory
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ItemDaoTest {
}
- Dalam class, tambahkan variabel
varpribadi dari jenisItemDaodanInventoryDatabase.
import com.example.inventory.data.InventoryDatabase
import com.example.inventory.data.ItemDao
private lateinit var itemDao: ItemDao
private lateinit var inventoryDatabase: InventoryDatabase
- Tambahkan fungsi untuk membuat database dan menganotasinya dengan
@Beforeagar dapat berjalan sebelum setiap pengujian. - Dalam metode, lakukan inisialisasi
itemDao.
import android.content.Context
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import org.junit.Before
@Before
fun createDb() {
val context: Context = ApplicationProvider.getApplicationContext()
// Using an in-memory database because the information stored here disappears when the
// process is killed.
inventoryDatabase = Room.inMemoryDatabaseBuilder(context, InventoryDatabase::class.java)
// Allowing main thread queries, just for testing.
.allowMainThreadQueries()
.build()
itemDao = inventoryDatabase.itemDao()
}
Dalam fungsi ini, Anda menggunakan database dalam memori dan tidak mempertahankannya di disk. Untuk melakukannya, gunakan fungsi inMemoryDatabaseBuilder(). Anda melakukan ini karena informasi tidak perlu dipertahankan, tetapi harus dihapus saat proses dihentikan. Anda menjalankan kueri DAO di thread utama dengan .allowMainThreadQueries(), hanya untuk pengujian.
- Tambahkan fungsi lain untuk menutup database. Anotasikan dengan
@Afteruntuk menutup database dan menjalankannya setelah setiap pengujian.
import org.junit.After
import java.io.IOException
@After
@Throws(IOException::class)
fun closeDb() {
inventoryDatabase.close()
}
- Deklarasikan item di class
ItemDaoTestyang akan digunakan database, seperti yang ditunjukkan dalam contoh kode berikut:
import com.example.inventory.data.Item
private var item1 = Item(1, "Apples", 10.0, 20)
private var item2 = Item(2, "Bananas", 15.0, 97)
- Tambahkan fungsi utilitas untuk menambahkan satu item, lalu dua item, ke database. Anda akan menggunakan fungsi ini nanti, dalam pengujian. Tandai sebagai
suspendagar dapat berjalan di coroutine.
private suspend fun addOneItemToDb() {
itemDao.insert(item1)
}
private suspend fun addTwoItemsToDb() {
itemDao.insert(item1)
itemDao.insert(item2)
}
- Tulis pengujian untuk menyisipkan satu item ke dalam database,
insert(). Beri nama pengujiandaoInsert_insertsItemIntoDBdan anotasikan dengan@Test.
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
@Test
@Throws(Exception::class)
fun daoInsert_insertsItemIntoDB() = runBlocking {
addOneItemToDb()
val allItems = itemDao.getAllItems().first()
assertEquals(allItems[0], item1)
}
Dalam pengujian ini, Anda menggunakan fungsi utilitas addOneItemToDb() untuk menambahkan satu item ke database. Kemudian, Anda akan membaca item pertama dalam database. Dengan assertEquals(), Anda membandingkan nilai yang diharapkan dengan nilai sebenarnya. Anda menjalankan pengujian dalam coroutine baru dengan runBlocking{}. Penyiapan ini adalah alasan Anda menandai fungsi utilitas sebagai suspend.
- Jalankan pengujian dan pastikan pengujian berhasil.


- Tulis pengujian lain untuk
getAllItems()dari database. Beri nama pengujiandaoGetAllItems_returnsAllItemsFromDB.
@Test
@Throws(Exception::class)
fun daoGetAllItems_returnsAllItemsFromDB() = runBlocking {
addTwoItemsToDb()
val allItems = itemDao.getAllItems().first()
assertEquals(allItems[0], item1)
assertEquals(allItems[1], item2)
}
Dalam pengujian di atas, Anda menambahkan dua item ke database di dalam coroutine. Kemudian, Anda membaca kedua item tersebut dan membandingkannya dengan nilai yang diharapkan.
6. Menampilkan detail item
Dalam tugas ini, Anda akan membaca dan menampilkan detail entity di layar Item Details. Anda menggunakan status UI item, seperti nama, harga, dan jumlah dari database aplikasi inventaris, lalu menampilkannya di layar Item Details dengan composable ItemDetailsScreen. Fungsi composable ItemDetailsScreen ditulis sebelumnya untuk Anda dan berisi tiga composable Teks yang menampilkan detail item.
ui/item/ItemDetailsScreen.kt
Layar ini adalah bagian dari kode awal dan menampilkan detail item, yang akan Anda lihat di codelab berikutnya. Anda tidak mengerjakan layar ini dalam codelab ini. ItemDetailsViewModel.kt adalah ViewModel yang sesuai untuk layar ini.

- Dalam fungsi composable
HomeScreen, perhatikan panggilan fungsiHomeBody().navigateToItemUpdatediteruskan ke parameteronItemClick, yang akan dipanggil saat Anda mengklik item mana pun dalam daftar.
// No need to copy over
HomeBody(
itemList = homeUiState.itemList,
onItemClick = navigateToItemUpdate,
modifier = modifier
.padding(innerPadding)
.fillMaxSize()
)
- Buka
ui/navigation/InventoryNavGraph.ktdan perhatikan parameternavigateToItemUpdatedi composableHomeScreen. Parameter ini menentukan tujuan untuk navigasi sebagai layar detail item.
// No need to copy over
HomeScreen(
navigateToItemEntry = { navController.navigate(ItemEntryDestination.route) },
navigateToItemUpdate = {
navController.navigate("${ItemDetailsDestination.route}/${it}")
}
Bagian dari fungsi onItemClick ini sudah diimplementasikan untuk Anda. Saat Anda mengklik item daftar, aplikasi akan membuka layar detail item.
- Klik item mana pun dalam daftar inventaris untuk melihat layar detail item dengan kolom kosong.

Untuk mengisi kolom teks dengan detail item, Anda harus mengumpulkan status UI di ItemDetailsScreen().
- Di
UI/Item/ItemDetailsScreen.kt, tambahkan parameter baru ke composableItemDetailsScreenjenisItemDetailsViewModeldan gunakan metode factory untuk menginisialisasinya.
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.inventory.ui.AppViewModelProvider
@Composable
fun ItemDetailsScreen(
navigateToEditItem: (Int) -> Unit,
navigateBack: () -> Unit,
modifier: Modifier = Modifier,
viewModel: ItemDetailsViewModel = viewModel(factory = AppViewModelProvider.Factory)
)
- Dalam composable
ItemDetailsScreen(), buatvalyang disebutuiStateuntuk mengumpulkan status UI. GunakancollectAsState()untuk mengumpulkanuiStateStateFlowdan mewakili nilai terbarunya melaluiState. Android Studio menampilkan error referensi yang belum terselesaikan.
import androidx.compose.runtime.collectAsState
val uiState = viewModel.uiState.collectAsState()
- Untuk mengatasi error, buat
valyang disebutuiStatedari jenisStateFlow<ItemDetailsUiState>di classItemDetailsViewModel. - Ambil data dari repositori item dan petakan ke
ItemDetailsUiStatemenggunakan fungsi ekstensitoItemDetails(). Fungsi ekstensiItem.toItemDetails()sudah ditulis untuk Anda sebagai bagian dari kode awal.
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
val uiState: StateFlow<ItemDetailsUiState> =
itemsRepository.getItemStream(itemId)
.filterNotNull()
.map {
ItemDetailsUiState(itemDetails = it.toItemDetails())
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
initialValue = ItemDetailsUiState()
)
- Teruskan
ItemsRepositorykeItemDetailsViewModeluntuk mengatasi errorUnresolved reference: itemsRepository.
class ItemDetailsViewModel(
savedStateHandle: SavedStateHandle,
private val itemsRepository: ItemsRepository
) : ViewModel() {
- Di
ui/AppViewModelProvider.kt, update penginisialisasi untukItemDetailsViewModelseperti ditunjukkan dalam cuplikan kode berikut:
initializer {
ItemDetailsViewModel(
this.createSavedStateHandle(),
inventoryApplication().container.itemsRepository
)
}
- Kembali ke
ItemDetailsScreen.ktdan perhatikan bahwa error dalam composableItemDetailsScreen()telah diselesaikan. - Pada composable
ItemDetailsScreen(), update panggilan fungsiItemDetailsBody()dan teruskan argumenuiState.valuekeitemUiState.
ItemDetailsBody(
itemUiState = uiState.value,
onSellItem = { },
onDelete = { },
modifier = modifier.padding(innerPadding)
)
- Amati implementasi
ItemDetailsBody()danItemInputForm(). Anda meneruskanitemyang saat ini dipilih dariItemDetailsBody()keItemDetails().
// No need to copy over
@Composable
private fun ItemDetailsBody(
itemUiState: ItemUiState,
onSellItem: () -> Unit,
onDelete: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
//...
) {
var deleteConfirmationRequired by rememberSaveable { mutableStateOf(false) }
ItemDetails(
item = itemDetailsUiState.itemDetails.toItem(), modifier = Modifier.fillMaxWidth()
)
//...
}
- Jalankan aplikasi. Saat Anda mengklik elemen daftar pada layar Inventory, layar Item Details akan ditampilkan.
- Perhatikan bahwa layar tidak kosong lagi. Halaman ini menampilkan detail entity yang diambil dari database inventaris.

- Ketuk tombol Sell. Tidak ada yang terjadi.
Di bagian berikutnya, Anda akan menerapkan fungsi tombol Sell.
7. Menerapkan layar Detail item
ui/item/ItemEditScreen.kt
Layar Item edit sudah disediakan untuk Anda sebagai bagian dari kode awal.
Tata letak ini berisi composable kolom teks untuk mengedit detail item inventaris baru.

Kode untuk aplikasi ini masih belum berfungsi sepenuhnya. Misalnya, di layar Item Details, saat Anda mengetuk tombol Sell, Quantity in Stock tidak akan berkurang. Saat Anda mengetuk tombol Delete, aplikasi akan memunculkan dialog konfirmasi. Namun, saat Anda memilih tombol Yes, aplikasi tidak benar-benar menghapus item.

Terakhir, tombol FAB
membuka layar Edit Item yang kosong.

Di bagian ini, Anda akan menerapkan fungsi tombol Sell, Delete, dan FAB.
8. Menerapkan item jual
Di bagian ini, Anda akan memperluas fitur aplikasi untuk mengimplementasikan fungsi penjualan. Update ini mencakup tugas-tugas berikut:
- Tambahkan pengujian untuk fungsi DAO guna mengupdate entity.
- Tambahkan fungsi di
ItemDetailsViewModeluntuk mengurangi jumlah dan mengupdate entity di database aplikasi. - Nonaktifkan tombol Sell jika jumlahnya nol.
- Di
ItemDaoTest.kt, tambahkan fungsi bernamadaoUpdateItems_updatesItemsInDB()tanpa parameter. Anotasi dengan@Testdan@Throws(Exception::class).
@Test
@Throws(Exception::class)
fun daoUpdateItems_updatesItemsInDB()
- Tentukan fungsi dan buat blok
runBlocking. PanggiladdTwoItemsToDb()di dalamnya.
fun daoUpdateItems_updatesItemsInDB() = runBlocking {
addTwoItemsToDb()
}
- Perbarui kedua entity dengan nilai yang berbeda, yang memanggil
itemDao.update.
itemDao.update(Item(1, "Apples", 15.0, 25))
itemDao.update(Item(2, "Bananas", 5.0, 50))
- Ambil entity dengan
itemDao.getAllItems(). Bandingkan dengan entity yang diupdate dan nyatakan.
val allItems = itemDao.getAllItems().first()
assertEquals(allItems[0], Item(1, "Apples", 15.0, 25))
assertEquals(allItems[1], Item(2, "Bananas", 5.0, 50))
- Pastikan fungsi yang sudah selesai terlihat seperti berikut:
@Test
@Throws(Exception::class)
fun daoUpdateItems_updatesItemsInDB() = runBlocking {
addTwoItemsToDb()
itemDao.update(Item(1, "Apples", 15.0, 25))
itemDao.update(Item(2, "Bananas", 5.0, 50))
val allItems = itemDao.getAllItems().first()
assertEquals(allItems[0], Item(1, "Apples", 15.0, 25))
assertEquals(allItems[1], Item(2, "Bananas", 5.0, 50))

