Add ViewModel tests

This commit is contained in:
Kirill Kamakin
2022-12-16 21:19:19 +01:00
parent 9a3a30aca2
commit c1a292851a
4 changed files with 57 additions and 15 deletions

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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()

View File

@@ -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()
}
}