diff --git a/.idea/.gitignore b/.idea/.gitignore index add5165..98fb131 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -11,3 +11,4 @@ /compiler.xml /androidTestResultsUserPreferences.xml /.name +/vcs.xml diff --git a/app/src/test/java/com/juandgaines/testground/data/UserRepositoryTest.kt b/app/src/test/java/com/juandgaines/testground/data/UserRepositoryTest.kt index 710752a..c705175 100644 --- a/app/src/test/java/com/juandgaines/testground/data/UserRepositoryTest.kt +++ b/app/src/test/java/com/juandgaines/testground/data/UserRepositoryTest.kt @@ -22,9 +22,6 @@ import retrofit2.Retrofit @OptIn(ExperimentalCoroutinesApi::class) class UserRepositoryTest { - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - private lateinit var repository: UserRepositoryImpl private lateinit var api: UserApiFake private lateinit var mockWebServer: MockWebServer diff --git a/app/src/test/java/com/juandgaines/testground/presentation/ProfileViewModelTest.kt b/app/src/test/java/com/juandgaines/testground/presentation/ProfileViewModelTest.kt new file mode 100644 index 0000000..e3f3a82 --- /dev/null +++ b/app/src/test/java/com/juandgaines/testground/presentation/ProfileViewModelTest.kt @@ -0,0 +1,88 @@ +package com.juandgaines.testground.presentation + +import androidx.lifecycle.SavedStateHandle +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Before +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class ProfileViewModelTest { + + private val testDispatcher = StandardTestDispatcher() + + private lateinit var viewModel: ProfileViewModel + private lateinit var repository: UserRepositoryFake + + @Before + fun setUp() { + Dispatchers.setMain(testDispatcher) + + repository = UserRepositoryFake() + viewModel = ProfileViewModel( + repository = repository, + savedStateHandle = SavedStateHandle( + initialState = mapOf( + "userId" to repository.profileToReturn.user.id + ) + ) + ) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun givenValidUserId_whenLoadProfile_thenProfileIsLoaded() = runTest { + // Act + viewModel.loadProfile() + advanceUntilIdle() + + // Assert + assertThat(viewModel.state.value.profile).isEqualTo(repository.profileToReturn) + assertThat(viewModel.state.value.isLoading).isFalse() + } + + @Test + fun givenRepositoryError_whenLoadProfile_thenErrorStateIsSet() = runTest { + // Arrange + repository.errorToReturn = Exception("Test exception") + + // Act + viewModel.loadProfile() + advanceUntilIdle() + + // Assert + assertThat(viewModel.state.value.profile).isNull() + assertThat(viewModel.state.value.errorMessage).isEqualTo("Test exception") + assertThat(viewModel.state.value.isLoading).isFalse() + } + + @Test + fun givenLoadingState_whenLoadProfile_thenStateUpdatesCorrectly() = runTest { + // Act & Assert + viewModel.state.test { + val emission1 = awaitItem() + assertThat(emission1.isLoading).isFalse() + + viewModel.loadProfile() + + val emission2 = awaitItem() + assertThat(emission2.isLoading).isTrue() + + val emission3 = awaitItem() + assertThat(emission3.isLoading).isFalse() + assertThat(emission3.profile).isEqualTo(repository.profileToReturn) + } + } +} diff --git a/app/src/test/java/com/juandgaines/testground/presentation/UserRepositoryFake.kt b/app/src/test/java/com/juandgaines/testground/presentation/UserRepositoryFake.kt new file mode 100644 index 0000000..4350d3d --- /dev/null +++ b/app/src/test/java/com/juandgaines/testground/presentation/UserRepositoryFake.kt @@ -0,0 +1,48 @@ +package com.juandgaines.testground.presentation + +import com.juandgaines.testground.domain.Coordinates +import com.juandgaines.testground.domain.Place +import com.juandgaines.testground.domain.Profile +import com.juandgaines.testground.domain.User +import com.juandgaines.testground.domain.UserRepository +import java.util.UUID + +class UserRepositoryFake : UserRepository { + var profileToReturn = profile() + var errorToReturn: Exception? = null + + override suspend fun getProfile(userId: String): Result { + return if (errorToReturn != null) { + Result.failure(errorToReturn!!) + } else Result.success(profileToReturn) + } + + override suspend fun getPlaces(userId: String): Result> { + return Result.success(profileToReturn.places) + } +} + +private fun user(): User { + return User( + id = UUID.randomUUID().toString(), + username = "test-user" + ) +} + +private fun place(userId: String): Place { + return Place( + id = UUID.randomUUID().toString(), + name = "test place", + coordinates = Coordinates(1.0, 1.0) + ) +} + +private fun profile(): Profile { + val user = user() + return Profile( + user = user, + places = (1..10).map { + place(user.id) + } + ) +} \ No newline at end of file