Add ViewModel tests
This commit is contained in:
@@ -72,6 +72,7 @@ class RecipesListViewModel @Inject constructor(
|
||||
logger.v { "onDeleteConfirm() called with: recipeSummaryEntity = $recipeSummaryEntity" }
|
||||
viewModelScope.launch {
|
||||
val result = recipeRepo.deleteRecipe(recipeSummaryEntity)
|
||||
logger.d { "onDeleteConfirm: delete result is $result" }
|
||||
_deleteRecipeResult.emit(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,23 @@ import com.google.common.truth.Truth.assertThat
|
||||
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||
import gq.kirmanak.mealient.data.recipes.RecipeRepo
|
||||
import gq.kirmanak.mealient.test.BaseUnitTest
|
||||
import gq.kirmanak.mealient.test.RecipeImplTestData.CAKE_RECIPE_SUMMARY_ENTITY
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.IOException
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class RecipesListViewModelTest : BaseUnitTest() {
|
||||
@@ -24,6 +32,12 @@ class RecipesListViewModelTest : BaseUnitTest() {
|
||||
@MockK(relaxed = true)
|
||||
lateinit var recipeRepo: RecipeRepo
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
every { authRepo.isAuthorizedFlow } returns flowOf(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when authRepo isAuthorized changes to true expect that recipes are refreshed`() {
|
||||
every { authRepo.isAuthorizedFlow } returns flowOf(false, true)
|
||||
@@ -40,14 +54,12 @@ class RecipesListViewModelTest : BaseUnitTest() {
|
||||
|
||||
@Test
|
||||
fun `when authRepo isAuthorized doesn't change expect that recipes are not refreshed`() {
|
||||
every { authRepo.isAuthorizedFlow } returns flowOf(true)
|
||||
createSubject()
|
||||
coVerify(inverse = true) { recipeRepo.refreshRecipes() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when refreshRecipeInfo succeeds expect successful result`() = runTest {
|
||||
every { authRepo.isAuthorizedFlow } returns flowOf(true)
|
||||
val slug = "cake"
|
||||
coEvery { recipeRepo.refreshRecipeInfo(eq(slug)) } returns Result.success(Unit)
|
||||
val actual = createSubject().refreshRecipeInfo(slug).asFlow().first()
|
||||
@@ -56,7 +68,6 @@ class RecipesListViewModelTest : BaseUnitTest() {
|
||||
|
||||
@Test
|
||||
fun `when refreshRecipeInfo succeeds expect call to repo`() = runTest {
|
||||
every { authRepo.isAuthorizedFlow } returns flowOf(true)
|
||||
val slug = "cake"
|
||||
coEvery { recipeRepo.refreshRecipeInfo(eq(slug)) } returns Result.success(Unit)
|
||||
createSubject().refreshRecipeInfo(slug).asFlow().first()
|
||||
@@ -65,7 +76,6 @@ class RecipesListViewModelTest : BaseUnitTest() {
|
||||
|
||||
@Test
|
||||
fun `when refreshRecipeInfo fails expect result with error`() = runTest {
|
||||
every { authRepo.isAuthorizedFlow } returns flowOf(true)
|
||||
val slug = "cake"
|
||||
val result = Result.failure<Unit>(RuntimeException())
|
||||
coEvery { recipeRepo.refreshRecipeInfo(eq(slug)) } returns result
|
||||
@@ -73,5 +83,38 @@ class RecipesListViewModelTest : BaseUnitTest() {
|
||||
assertThat(actual).isEqualTo(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when delete recipe expect successful result in flow`() = runTest {
|
||||
coEvery { recipeRepo.deleteRecipe(any()) } returns Result.success(Unit)
|
||||
val subject = createSubject()
|
||||
val results = runTestAndCollectFlow(subject.deleteRecipeResult) {
|
||||
subject.onDeleteConfirm(CAKE_RECIPE_SUMMARY_ENTITY)
|
||||
}
|
||||
assertThat(results.single().isSuccess).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when delete recipe expect failed result in flow`() = runTest {
|
||||
coEvery { recipeRepo.deleteRecipe(any()) } returns Result.failure(IOException())
|
||||
val subject = createSubject()
|
||||
val results = runTestAndCollectFlow(subject.deleteRecipeResult) {
|
||||
subject.onDeleteConfirm(CAKE_RECIPE_SUMMARY_ENTITY)
|
||||
}
|
||||
assertThat(results.single().isFailure).isTrue()
|
||||
}
|
||||
|
||||
private inline fun <T> TestScope.runTestAndCollectFlow(
|
||||
flow: Flow<T>,
|
||||
block: () -> Unit,
|
||||
): List<T> {
|
||||
val results = mutableListOf<T>()
|
||||
val collectJob = launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
flow.toList(results)
|
||||
}
|
||||
block()
|
||||
collectJob.cancel()
|
||||
return results
|
||||
}
|
||||
|
||||
private fun createSubject() = RecipesListViewModel(recipeRepo, authRepo, logger)
|
||||
}
|
||||
@@ -15,9 +15,7 @@ import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.Timeout
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ShareRecipeViewModelTest : BaseUnitTest() {
|
||||
@@ -27,9 +25,6 @@ class ShareRecipeViewModelTest : BaseUnitTest() {
|
||||
|
||||
lateinit var subject: ShareRecipeViewModel
|
||||
|
||||
@get:Rule
|
||||
val timeoutRule: Timeout = Timeout.seconds(5)
|
||||
|
||||
@Before
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
@@ -7,20 +7,23 @@ import io.mockk.MockKAnnotations
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.rules.Timeout
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
open class BaseUnitTest {
|
||||
|
||||
@get:Rule
|
||||
@get:Rule(order = 0)
|
||||
val instantExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@get:Rule(order = 1)
|
||||
val timeoutRule: Timeout = Timeout.seconds(10)
|
||||
|
||||
protected val logger: Logger = FakeLogger()
|
||||
|
||||
lateinit var dispatchers: AppDispatchers
|
||||
@@ -30,10 +33,10 @@ open class BaseUnitTest {
|
||||
MockKAnnotations.init(this)
|
||||
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()
|
||||
override val io: CoroutineDispatcher = UnconfinedTestDispatcher()
|
||||
override val main: CoroutineDispatcher = UnconfinedTestDispatcher()
|
||||
override val default: CoroutineDispatcher = UnconfinedTestDispatcher()
|
||||
override val unconfined: CoroutineDispatcher = UnconfinedTestDispatcher()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user