Update unit tests

This commit is contained in:
Kirill Kamakin
2022-12-13 21:32:39 +01:00
parent c6142c6218
commit 127153cac7
12 changed files with 59 additions and 33 deletions

View File

@@ -4,7 +4,7 @@ import androidx.annotation.VisibleForTesting
import androidx.paging.* import androidx.paging.*
import androidx.paging.LoadType.PREPEND import androidx.paging.LoadType.PREPEND
import androidx.paging.LoadType.REFRESH import androidx.paging.LoadType.REFRESH
import gq.kirmanak.mealient.data.configuration.AppDispatchers import gq.kirmanak.mealient.architecture.configuration.AppDispatchers
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity

View File

@@ -5,8 +5,6 @@ import dagger.Module
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
import gq.kirmanak.mealient.data.configuration.AppDispatchers
import gq.kirmanak.mealient.data.configuration.AppDispatchersImpl
import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl
import javax.inject.Singleton import javax.inject.Singleton
@@ -17,8 +15,4 @@ interface ArchitectureModule {
@Binds @Binds
@Singleton @Singleton
fun bindBuildConfiguration(buildConfigurationImpl: BuildConfigurationImpl): BuildConfiguration fun bindBuildConfiguration(buildConfigurationImpl: BuildConfigurationImpl): BuildConfiguration
@Binds
@Singleton
fun bindAppDispatchers(appDispatchersImpl: AppDispatchersImpl): AppDispatchers
} }

View File

@@ -14,9 +14,7 @@ import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_CAKE_RECIPE_INSTRUCTION_
import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_INSTRUCTION import gq.kirmanak.mealient.test.RecipeImplTestData.MIX_INSTRUCTION
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_FULL_RECIPE_INFO import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_FULL_RECIPE_INFO
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealient.test.RecipeImplTestData.RECIPE_SUMMARY_CAKE import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARY_ENTITIES
import gq.kirmanak.mealient.test.RecipeImplTestData.RECIPE_SUMMARY_PORRIDGE_V0
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARIES
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Test import org.junit.Test
@@ -34,7 +32,7 @@ class RecipeStorageImplTest : HiltRobolectricTest() {
@Test @Test
fun `when saveRecipes then saves recipes`() = runTest { fun `when saveRecipes then saves recipes`() = runTest {
subject.saveRecipes(TEST_RECIPE_SUMMARIES) subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
val actualTags = appDb.recipeDao().queryAllRecipes() val actualTags = appDb.recipeDao().queryAllRecipes()
assertThat(actualTags).containsExactly( assertThat(actualTags).containsExactly(
CAKE_RECIPE_SUMMARY_ENTITY, CAKE_RECIPE_SUMMARY_ENTITY,
@@ -44,15 +42,15 @@ class RecipeStorageImplTest : HiltRobolectricTest() {
@Test @Test
fun `when refreshAll then old recipes aren't preserved`() = runTest { fun `when refreshAll then old recipes aren't preserved`() = runTest {
subject.saveRecipes(TEST_RECIPE_SUMMARIES) subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
subject.refreshAll(listOf(RECIPE_SUMMARY_CAKE)) subject.refreshAll(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
val actual = appDb.recipeDao().queryAllRecipes() val actual = appDb.recipeDao().queryAllRecipes()
assertThat(actual).containsExactly(CAKE_RECIPE_SUMMARY_ENTITY) assertThat(actual).containsExactly(CAKE_RECIPE_SUMMARY_ENTITY)
} }
@Test @Test
fun `when clearAllLocalData then recipes aren't preserved`() = runTest { fun `when clearAllLocalData then recipes aren't preserved`() = runTest {
subject.saveRecipes(TEST_RECIPE_SUMMARIES) subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
subject.clearAllLocalData() subject.clearAllLocalData()
val actual = appDb.recipeDao().queryAllRecipes() val actual = appDb.recipeDao().queryAllRecipes()
assertThat(actual).isEmpty() assertThat(actual).isEmpty()
@@ -60,7 +58,7 @@ class RecipeStorageImplTest : HiltRobolectricTest() {
@Test @Test
fun `when saveRecipeInfo then saves recipe info`() = runTest { fun `when saveRecipeInfo then saves recipe info`() = runTest {
subject.saveRecipes(listOf(RECIPE_SUMMARY_CAKE)) subject.saveRecipes(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO) subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
val actual = appDb.recipeDao().queryFullRecipeInfo("1") val actual = appDb.recipeDao().queryFullRecipeInfo("1")
assertThat(actual).isEqualTo(FULL_CAKE_INFO_ENTITY) assertThat(actual).isEqualTo(FULL_CAKE_INFO_ENTITY)
@@ -68,7 +66,7 @@ class RecipeStorageImplTest : HiltRobolectricTest() {
@Test @Test
fun `when saveRecipeInfo with two then saves second`() = runTest { fun `when saveRecipeInfo with two then saves second`() = runTest {
subject.saveRecipes(listOf(RECIPE_SUMMARY_CAKE, RECIPE_SUMMARY_PORRIDGE_V0)) subject.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO) subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
subject.saveRecipeInfo(PORRIDGE_FULL_RECIPE_INFO) subject.saveRecipeInfo(PORRIDGE_FULL_RECIPE_INFO)
val actual = appDb.recipeDao().queryFullRecipeInfo("2") val actual = appDb.recipeDao().queryFullRecipeInfo("2")
@@ -77,7 +75,7 @@ class RecipeStorageImplTest : HiltRobolectricTest() {
@Test @Test
fun `when saveRecipeInfo secondly then overwrites ingredients`() = runTest { fun `when saveRecipeInfo secondly then overwrites ingredients`() = runTest {
subject.saveRecipes(listOf(RECIPE_SUMMARY_CAKE)) subject.saveRecipes(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO) subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
val newRecipe = CAKE_FULL_RECIPE_INFO.copy(recipeIngredients = listOf(BREAD_INGREDIENT)) val newRecipe = CAKE_FULL_RECIPE_INFO.copy(recipeIngredients = listOf(BREAD_INGREDIENT))
subject.saveRecipeInfo(newRecipe) subject.saveRecipeInfo(newRecipe)
@@ -88,7 +86,7 @@ class RecipeStorageImplTest : HiltRobolectricTest() {
@Test @Test
fun `when saveRecipeInfo secondly then overwrites instructions`() = runTest { fun `when saveRecipeInfo secondly then overwrites instructions`() = runTest {
subject.saveRecipes(listOf(RECIPE_SUMMARY_CAKE)) subject.saveRecipes(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO) subject.saveRecipeInfo(CAKE_FULL_RECIPE_INFO)
val newRecipe = CAKE_FULL_RECIPE_INFO.copy(recipeInstructions = listOf(MIX_INSTRUCTION)) val newRecipe = CAKE_FULL_RECIPE_INFO.copy(recipeInstructions = listOf(MIX_INSTRUCTION))
subject.saveRecipeInfo(newRecipe) subject.saveRecipeInfo(newRecipe)

View File

@@ -8,7 +8,6 @@ import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
import gq.kirmanak.mealient.test.HiltRobolectricTest import gq.kirmanak.mealient.test.HiltRobolectricTest
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY import gq.kirmanak.mealient.test.RecipeImplTestData.PORRIDGE_RECIPE_SUMMARY_ENTITY
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARIES
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARY_ENTITIES import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARY_ENTITIES
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
@@ -27,28 +26,28 @@ class RecipePagingSourceFactoryImplTest : HiltRobolectricTest() {
@Test @Test
fun `when query is ca expect cake only is returned`() = runTest { fun `when query is ca expect cake only is returned`() = runTest {
storage.saveRecipes(TEST_RECIPE_SUMMARIES) storage.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
subject.setQuery("ca") subject.setQuery("ca")
assertThat(queryRecipes()).isEqualTo(listOf(CAKE_RECIPE_SUMMARY_ENTITY)) assertThat(queryRecipes()).isEqualTo(listOf(CAKE_RECIPE_SUMMARY_ENTITY))
} }
@Test @Test
fun `when query is po expect porridge only is returned`() = runTest { fun `when query is po expect porridge only is returned`() = runTest {
storage.saveRecipes(TEST_RECIPE_SUMMARIES) storage.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
subject.setQuery("po") subject.setQuery("po")
assertThat(queryRecipes()).isEqualTo(listOf(PORRIDGE_RECIPE_SUMMARY_ENTITY)) assertThat(queryRecipes()).isEqualTo(listOf(PORRIDGE_RECIPE_SUMMARY_ENTITY))
} }
@Test @Test
fun `when query is e expect cake and porridge are returned`() = runTest { fun `when query is e expect cake and porridge are returned`() = runTest {
storage.saveRecipes(TEST_RECIPE_SUMMARIES) storage.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
subject.setQuery("e") subject.setQuery("e")
assertThat(queryRecipes()).isEqualTo(TEST_RECIPE_SUMMARY_ENTITIES) assertThat(queryRecipes()).isEqualTo(TEST_RECIPE_SUMMARY_ENTITIES)
} }
@Test @Test
fun `when query is null expect cake and porridge are returned`() = runTest { fun `when query is null expect cake and porridge are returned`() = runTest {
storage.saveRecipes(TEST_RECIPE_SUMMARIES) storage.saveRecipes(TEST_RECIPE_SUMMARY_ENTITIES)
subject.setQuery(null) subject.setQuery(null)
assertThat(queryRecipes()).isEqualTo(TEST_RECIPE_SUMMARY_ENTITIES) assertThat(queryRecipes()).isEqualTo(TEST_RECIPE_SUMMARY_ENTITIES)
} }

View File

@@ -9,6 +9,7 @@ import gq.kirmanak.mealient.database.recipe.entity.RecipeSummaryEntity
import gq.kirmanak.mealient.datasource.NetworkError.Unauthorized import gq.kirmanak.mealient.datasource.NetworkError.Unauthorized
import gq.kirmanak.mealient.test.BaseUnitTest import gq.kirmanak.mealient.test.BaseUnitTest
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARIES import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARIES
import gq.kirmanak.mealient.test.RecipeImplTestData.TEST_RECIPE_SUMMARY_ENTITIES
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.coVerify import io.mockk.coVerify
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
@@ -42,7 +43,14 @@ class RecipesRemoteMediatorTest : BaseUnitTest() {
@Before @Before
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
subject = RecipesRemoteMediator(storage, dataSource, pagingSourceFactory, logger) subject = RecipesRemoteMediator(
storage = storage,
network = dataSource,
pagingSourceFactory = pagingSourceFactory,
logger = logger,
dispatchers = dispatchers,
)
coEvery { dataSource.getFavoriteRecipes() } returns emptyList()
} }
@Test @Test
@@ -70,7 +78,7 @@ class RecipesRemoteMediatorTest : BaseUnitTest() {
fun `when first load with refresh successful then recipes stored`() = runTest { fun `when first load with refresh successful then recipes stored`() = runTest {
coEvery { dataSource.requestRecipes(eq(0), eq(6)) } returns TEST_RECIPE_SUMMARIES coEvery { dataSource.requestRecipes(eq(0), eq(6)) } returns TEST_RECIPE_SUMMARIES
subject.load(REFRESH, pagingState()) subject.load(REFRESH, pagingState())
coVerify { storage.refreshAll(eq(TEST_RECIPE_SUMMARIES)) } coVerify { storage.refreshAll(eq(TEST_RECIPE_SUMMARY_ENTITIES)) }
} }
@Test @Test
@@ -132,9 +140,7 @@ class RecipesRemoteMediatorTest : BaseUnitTest() {
subject.load(REFRESH, pagingState()) subject.load(REFRESH, pagingState())
coEvery { dataSource.requestRecipes(any(), any()) } throws Unauthorized(RuntimeException()) coEvery { dataSource.requestRecipes(any(), any()) } throws Unauthorized(RuntimeException())
subject.load(APPEND, pagingState()) subject.load(APPEND, pagingState())
coVerify { coVerify { storage.refreshAll(TEST_RECIPE_SUMMARY_ENTITIES) }
storage.refreshAll(TEST_RECIPE_SUMMARIES)
}
} }
private fun pagingState( private fun pagingState(

View File

@@ -76,7 +76,7 @@ class ModelMappingsTest : BaseUnitTest() {
@Test @Test
fun `when summary info to entity expect correct entity`() { fun `when summary info to entity expect correct entity`() {
val actual = RECIPE_SUMMARY_PORRIDGE_V0.toRecipeSummaryEntity() val actual = RECIPE_SUMMARY_PORRIDGE_V0.toRecipeSummaryEntity(isFavorite = false)
assertThat(actual).isEqualTo(PORRIDGE_RECIPE_SUMMARY_ENTITY) assertThat(actual).isEqualTo(PORRIDGE_RECIPE_SUMMARY_ENTITY)
} }

View File

@@ -80,6 +80,7 @@ object RecipeImplTestData {
dateAdded = LocalDate.parse("2021-11-13"), dateAdded = LocalDate.parse("2021-11-13"),
dateUpdated = LocalDateTime.parse("2021-11-13T15:30:13"), dateUpdated = LocalDateTime.parse("2021-11-13T15:30:13"),
imageId = "cake", imageId = "cake",
isFavorite = false,
) )
val PORRIDGE_RECIPE_SUMMARY_ENTITY = RecipeSummaryEntity( val PORRIDGE_RECIPE_SUMMARY_ENTITY = RecipeSummaryEntity(
@@ -90,6 +91,7 @@ object RecipeImplTestData {
dateAdded = LocalDate.parse("2021-11-12"), dateAdded = LocalDate.parse("2021-11-12"),
dateUpdated = LocalDateTime.parse("2021-10-13T17:35:23"), dateUpdated = LocalDateTime.parse("2021-10-13T17:35:23"),
imageId = "porridge", imageId = "porridge",
isFavorite = false,
) )
val TEST_RECIPE_SUMMARY_ENTITIES = val TEST_RECIPE_SUMMARY_ENTITIES =

View File

@@ -1,11 +1,10 @@
package gq.kirmanak.mealient.data.configuration package gq.kirmanak.mealient.architecture.configuration
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.MainCoroutineDispatcher
interface AppDispatchers { interface AppDispatchers {
val io: CoroutineDispatcher val io: CoroutineDispatcher
val main: MainCoroutineDispatcher val main: CoroutineDispatcher
val default: CoroutineDispatcher val default: CoroutineDispatcher
val unconfined: CoroutineDispatcher val unconfined: CoroutineDispatcher
} }

View File

@@ -1,4 +1,4 @@
package gq.kirmanak.mealient.data.configuration package gq.kirmanak.mealient.architecture.configuration
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import javax.inject.Inject import javax.inject.Inject

View File

@@ -0,0 +1,16 @@
package gq.kirmanak.mealient.architecture.configuration
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
interface ArchitectureModule {
@Binds
@Singleton
fun bindAppDispatchers(appDispatchersImpl: AppDispatchersImpl): AppDispatchers
}

View File

@@ -15,6 +15,7 @@ android {
dependencies { dependencies {
implementation(project(":logging")) implementation(project(":logging"))
implementation(project(":architecture"))
implementation(libs.google.dagger.hiltAndroid) implementation(libs.google.dagger.hiltAndroid)
kapt(libs.google.dagger.hiltCompiler) kapt(libs.google.dagger.hiltCompiler)

View File

@@ -1,10 +1,13 @@
package gq.kirmanak.mealient.test package gq.kirmanak.mealient.test
import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import gq.kirmanak.mealient.architecture.configuration.AppDispatchers
import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.logging.Logger
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain import kotlinx.coroutines.test.setMain
@@ -20,10 +23,18 @@ open class BaseUnitTest {
protected val logger: Logger = FakeLogger() protected val logger: Logger = FakeLogger()
lateinit var dispatchers: AppDispatchers
@Before @Before
open fun setUp() { open fun setUp() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
Dispatchers.setMain(UnconfinedTestDispatcher()) Dispatchers.setMain(UnconfinedTestDispatcher())
dispatchers = object : AppDispatchers {
override val io: CoroutineDispatcher = StandardTestDispatcher()
override val main: CoroutineDispatcher = StandardTestDispatcher()
override val default: CoroutineDispatcher = StandardTestDispatcher()
override val unconfined: CoroutineDispatcher = StandardTestDispatcher()
}
} }
@After @After