diff --git a/buildSrc/src/main/kotlin/AppConfig.kt b/buildSrc/src/main/kotlin/AppConfig.kt index b34cc0d..c8bc9a3 100644 --- a/buildSrc/src/main/kotlin/AppConfig.kt +++ b/buildSrc/src/main/kotlin/AppConfig.kt @@ -10,4 +10,6 @@ object AppConfig { const val versionCode: Int = 1 const val versionName: String = "1.0" -} \ No newline at end of file + + const val testInstrumentationRunner: String = "androidx.test.runner.AndroidJUnitRunner" +} diff --git a/buildSrc/src/main/kotlin/Library.kt b/buildSrc/src/main/kotlin/Library.kt index 888cf0c..e464857 100644 --- a/buildSrc/src/main/kotlin/Library.kt +++ b/buildSrc/src/main/kotlin/Library.kt @@ -55,6 +55,10 @@ object Library { // Mockito const val mockito = "org.mockito.kotlin:mockito-kotlin:${Version.mockito}" + + // AndroidXTest + const val androidXTest: String = "androidx.test.ext:junit:${Version.testExt}" + const val robolectric: String = "org.robolectric:robolectric:${Version.robolectric}" } object Project { diff --git a/buildSrc/src/main/kotlin/Version.kt b/buildSrc/src/main/kotlin/Version.kt index 810aa4e..ff789eb 100644 --- a/buildSrc/src/main/kotlin/Version.kt +++ b/buildSrc/src/main/kotlin/Version.kt @@ -33,6 +33,10 @@ object Version { // Mockito const val mockito: String = "3.2.0" + // AndroidXTest + const val testExt: String = "1.1.3" + const val robolectric: String = "4.5.1" + // ktlint const val ktlint: String = "0.42.1" } diff --git a/buildSrc/src/main/kotlin/extensions/AndroidAppExtension.kt b/buildSrc/src/main/kotlin/extensions/AndroidAppExtension.kt index 1c2bbcf..7b08290 100644 --- a/buildSrc/src/main/kotlin/extensions/AndroidAppExtension.kt +++ b/buildSrc/src/main/kotlin/extensions/AndroidAppExtension.kt @@ -22,6 +22,7 @@ private class AndroidAppExtension : ProjectExtension { minSdk = AppConfig.minSdkVersion versionCode(AppConfig.versionCode) versionName(AppConfig.versionName) + testInstrumentationRunner = AppConfig.testInstrumentationRunner } buildTypes { diff --git a/buildSrc/src/main/kotlin/plugin/AndroidLibraryPlugin.kt b/buildSrc/src/main/kotlin/plugin/AndroidLibraryPlugin.kt index 22f5ae9..7540a89 100644 --- a/buildSrc/src/main/kotlin/plugin/AndroidLibraryPlugin.kt +++ b/buildSrc/src/main/kotlin/plugin/AndroidLibraryPlugin.kt @@ -25,8 +25,7 @@ class AndroidLibraryPlugin : BasePlugin() { get() = { implementation( Library.daggerHiltAndroid, - Library.coroutines, - Library.room + Library.coroutines ) testImplementation( Library.junit, @@ -34,10 +33,7 @@ class AndroidLibraryPlugin : BasePlugin() { Library.mockito, Library.coroutinesTest ) - kapt( - Library.daggerHiltCompiler, - Library.roomCompiler - ) + kapt(Library.daggerHiltCompiler) } override val extensions: Array diff --git a/cache/build.gradle.kts b/cache/build.gradle.kts index d61e778..ec607c1 100644 --- a/cache/build.gradle.kts +++ b/cache/build.gradle.kts @@ -1,3 +1,13 @@ plugins { androidLib } + +dependencies { + implementation(Library.room) + kapt(Library.roomCompiler) + + testImplementation( + Library.androidXTest, + Library.robolectric + ) +} diff --git a/cache/src/main/java/com/example/expenselogger/cache/dao/ExpenseDao.kt b/cache/src/main/java/com/example/expenselogger/cache/dao/ExpenseDao.kt index 1001633..ee81902 100644 --- a/cache/src/main/java/com/example/expenselogger/cache/dao/ExpenseDao.kt +++ b/cache/src/main/java/com/example/expenselogger/cache/dao/ExpenseDao.kt @@ -2,24 +2,25 @@ package com.example.expenselogger.cache.dao import androidx.room.Dao import androidx.room.Insert +import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import com.example.expenselogger.cache.entity.ExpenseEntity @Dao internal interface ExpenseDao { - @Insert - fun insertExpense(expense: ExpenseEntity) + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertExpense(expense: ExpenseEntity): Long @Update - fun updateExpense(expense: ExpenseEntity) + suspend fun updateExpense(expense: ExpenseEntity) @Query("SELECT * FROM expense WHERE id = :id") - fun getExpense(id: Long): ExpenseEntity + suspend fun getExpense(id: Long): ExpenseEntity? @Query("SELECT * FROM expense ORDER BY date ASC") - public fun getExpenses(): List + suspend fun getExpenses(): List @Query("DELETE FROM expense WHERE id = :id") - fun deleteExpense(id: Long) + suspend fun deleteExpense(id: Long) } diff --git a/cache/src/main/java/com/example/expenselogger/cache/entity/ExpenseEntity.kt b/cache/src/main/java/com/example/expenselogger/cache/entity/ExpenseEntity.kt index c7f905d..4e24152 100644 --- a/cache/src/main/java/com/example/expenselogger/cache/entity/ExpenseEntity.kt +++ b/cache/src/main/java/com/example/expenselogger/cache/entity/ExpenseEntity.kt @@ -6,9 +6,6 @@ import androidx.room.PrimaryKey @Entity(tableName = "expense") public data class ExpenseEntity( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(name = "id") - val id: Long, @ColumnInfo(name = "name") val name: String, @ColumnInfo(name = "amount") @@ -16,5 +13,8 @@ public data class ExpenseEntity( @ColumnInfo(name = "date") val date: Long, @ColumnInfo(name = "info") - val info: String + val info: String, + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = "id") + val id: Long = 0L ) diff --git a/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepository.kt b/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepository.kt index 38bdb20..beab0b5 100644 --- a/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepository.kt +++ b/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepository.kt @@ -3,10 +3,9 @@ package com.example.expenselogger.cache.repository import com.example.expenselogger.cache.entity.ExpenseEntity public interface ExpenseRepository { - - public fun insertExpense(expenseEntity: ExpenseEntity) - public fun updateExpense(expenseEntity: ExpenseEntity) - public fun getExpense(id: Long): ExpenseEntity? - public fun getExpenses(): List - public fun deleteExpense(id: Long) + public suspend fun insertExpense(expenseEntity: ExpenseEntity): Long + public suspend fun updateExpense(expenseEntity: ExpenseEntity) + public suspend fun getExpense(id: Long): ExpenseEntity? + public suspend fun getExpenses(): List + public suspend fun deleteExpense(id: Long) } diff --git a/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepositoryImpl.kt b/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepositoryImpl.kt index 3f376ea..0fa7c26 100644 --- a/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepositoryImpl.kt +++ b/cache/src/main/java/com/example/expenselogger/cache/repository/ExpenseRepositoryImpl.kt @@ -8,23 +8,21 @@ internal class ExpenseRepositoryImpl @Inject constructor( private val expenseDao: ExpenseDao ) : ExpenseRepository { - override fun insertExpense(expenseEntity: ExpenseEntity) { - expenseDao.insertExpense(expenseEntity) - } + override suspend fun insertExpense( + expenseEntity: ExpenseEntity + ): Long = expenseDao.insertExpense(expenseEntity) - override fun updateExpense(expenseEntity: ExpenseEntity) { - expenseDao.updateExpense(expenseEntity) - } + override suspend fun updateExpense( + expenseEntity: ExpenseEntity + ) = expenseDao.updateExpense(expenseEntity) - override fun getExpense(id: Long): ExpenseEntity? { - return expenseDao.getExpense(id) - } + override suspend fun getExpense( + id: Long + ): ExpenseEntity? = expenseDao.getExpense(id) - override fun getExpenses(): List { - return expenseDao.getExpenses() - } + override suspend fun getExpenses(): List = expenseDao.getExpenses() - override fun deleteExpense(id: Long) { + override suspend fun deleteExpense(id: Long) { expenseDao.deleteExpense(id) } } diff --git a/cache/src/test/java/com/example/expenselogger/cache/entity/DummyData.kt b/cache/src/test/java/com/example/expenselogger/cache/entity/DummyData.kt new file mode 100644 index 0000000..4248683 --- /dev/null +++ b/cache/src/test/java/com/example/expenselogger/cache/entity/DummyData.kt @@ -0,0 +1,13 @@ +package com.example.expenselogger.cache.entity + +import java.util.Date + +internal object DummyData { + + val expenseEntity: ExpenseEntity = ExpenseEntity( + name = "Valentine outing", + amount = 13_500.00, + date = Date(1613311218000).time, // February 14th 2021 + info = "Valentine outing with bae" + ) +} diff --git a/cache/src/test/java/com/example/expenselogger/cache/repository/ExpenseRepositoryTest.kt b/cache/src/test/java/com/example/expenselogger/cache/repository/ExpenseRepositoryTest.kt new file mode 100644 index 0000000..7ed24fa --- /dev/null +++ b/cache/src/test/java/com/example/expenselogger/cache/repository/ExpenseRepositoryTest.kt @@ -0,0 +1,88 @@ +package com.example.expenselogger.cache.repository + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.example.expenselogger.cache.ExpenseDatabase +import com.example.expenselogger.cache.entity.DummyData +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.runBlocking +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +internal class ExpenseRepositoryTest { + + private lateinit var expenseRepository: ExpenseRepository + private lateinit var expenseDatabase: ExpenseDatabase + + @Before + fun setup() { + expenseDatabase = Room.inMemoryDatabaseBuilder( + ApplicationProvider.getApplicationContext(), + ExpenseDatabase::class.java + ).allowMainThreadQueries().build() + + expenseRepository = ExpenseRepositoryImpl( + expenseDatabase.expenseDao + ) + } + + @After + fun `tearDown()`() { + expenseDatabase.close() + } + + @Test + fun `verify that insertExpense inserts Expense into database`(): Unit = + runBlocking { + val expenseEntity = DummyData.expenseEntity + val id = expenseRepository.insertExpense(expenseEntity) + val actual = expenseRepository.getExpense(id) + assertThat(actual).isEqualTo(expenseEntity.copy(id = id)) + } + + @Test + fun `verify that updateExpense updates an Expense in the database`(): Unit = + runBlocking { + val expenseEntity = DummyData.expenseEntity + val id = expenseRepository.insertExpense(expenseEntity) + val newInfo = "Valentine outing with now ex bae" + val expenseEntityUpdate = expenseEntity.copy(id = id, info = newInfo) + expenseRepository.updateExpense(expenseEntityUpdate) + val actual = expenseRepository.getExpense(id) + assertThat(actual).isEqualTo(expenseEntityUpdate) + } + + @Test + fun `verify that getExpenses gets list of expenses`(): Unit = runBlocking { + val expense = DummyData.expenseEntity + val id = expenseRepository.insertExpense(expense) + val id2 = expenseRepository.insertExpense(expense) + val actual = expenseRepository.getExpenses() + val expected = listOf( + expense.copy(id = id), + expense.copy(id = id2) + ) + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `verify that getExpense gets an expense`(): Unit = runBlocking { + val expenseEntity = DummyData.expenseEntity + val id = expenseRepository.insertExpense(expenseEntity) + val actual = expenseRepository.getExpense(id) + assertThat(actual).isEqualTo(expenseEntity.copy(id = id)) + } + + @Test + fun `verify that deleteExpense deletes an expense`(): Unit = runBlocking { + val expenseEntity = DummyData.expenseEntity + val id = expenseRepository.insertExpense(expenseEntity) + expenseRepository.deleteExpense(id) + val actual = expenseRepository.getExpense(id) + assertThat(actual).isNull() + } +}