Issue#41 Copy full example into separate module with Hilt Integration
This commit is contained in:
parent
69e76dc0da
commit
52a99a82fc
229 changed files with 8416 additions and 11 deletions
|
|
@ -0,0 +1,216 @@
|
|||
package org.fnives.test.showcase.hilt.ui.auth
|
||||
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.fnives.test.showcase.android.testutil.InstantExecutorExtension
|
||||
import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher
|
||||
import org.fnives.test.showcase.hilt.core.login.LoginUseCase
|
||||
import org.fnives.test.showcase.hilt.ui.shared.Event
|
||||
import org.fnives.test.showcase.model.auth.LoginCredentials
|
||||
import org.fnives.test.showcase.model.auth.LoginStatus
|
||||
import org.fnives.test.showcase.model.shared.Answer
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.Arguments
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
import org.mockito.kotlin.anyOrNull
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
import org.mockito.kotlin.whenever
|
||||
import java.util.stream.Stream
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class)
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class AuthViewModelTest {
|
||||
|
||||
private lateinit var sut: AuthViewModel
|
||||
private lateinit var mockLoginUseCase: LoginUseCase
|
||||
private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockLoginUseCase = mock()
|
||||
sut = AuthViewModel(mockLoginUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN initialized viewModel WHEN observed THEN loading false other fields are empty")
|
||||
@Test
|
||||
fun initialSetup() {
|
||||
val usernameTestObserver = sut.username.test()
|
||||
val passwordTestObserver = sut.password.test()
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val errorTestObserver = sut.error.test()
|
||||
val navigateToHomeTestObserver = sut.navigateToHome.test()
|
||||
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
usernameTestObserver.assertNoValue()
|
||||
passwordTestObserver.assertNoValue()
|
||||
loadingTestObserver.assertValue(false)
|
||||
errorTestObserver.assertNoValue()
|
||||
navigateToHomeTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN password text WHEN onPasswordChanged is called THEN password livedata is updated")
|
||||
@Test
|
||||
fun whenPasswordChangedLiveDataIsUpdated() {
|
||||
val usernameTestObserver = sut.username.test()
|
||||
val passwordTestObserver = sut.password.test()
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val errorTestObserver = sut.error.test()
|
||||
val navigateToHomeTestObserver = sut.navigateToHome.test()
|
||||
|
||||
sut.onPasswordChanged("a")
|
||||
sut.onPasswordChanged("al")
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
usernameTestObserver.assertNoValue()
|
||||
passwordTestObserver.assertValueHistory("a", "al")
|
||||
loadingTestObserver.assertValue(false)
|
||||
errorTestObserver.assertNoValue()
|
||||
navigateToHomeTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN username text WHEN onUsernameChanged is called THEN username livedata is updated")
|
||||
@Test
|
||||
fun whenUsernameChangedLiveDataIsUpdated() {
|
||||
val usernameTestObserver = sut.username.test()
|
||||
val passwordTestObserver = sut.password.test()
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val errorTestObserver = sut.error.test()
|
||||
val navigateToHomeTestObserver = sut.navigateToHome.test()
|
||||
|
||||
sut.onUsernameChanged("bla")
|
||||
sut.onUsernameChanged("blabla")
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
usernameTestObserver.assertValueHistory("bla", "blabla")
|
||||
passwordTestObserver.assertNoValue()
|
||||
loadingTestObserver.assertValue(false)
|
||||
errorTestObserver.assertNoValue()
|
||||
navigateToHomeTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN no password or username WHEN login is Called THEN empty credentials are used in usecase")
|
||||
@Test
|
||||
fun noPasswordUsesEmptyStringInLoginUseCase() {
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
runBlocking {
|
||||
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Error(Throwable()))
|
||||
}
|
||||
|
||||
sut.onLogin()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
loadingTestObserver.assertValueHistory(false, true, false)
|
||||
runBlocking { verify(mockLoginUseCase, times(1)).invoke(LoginCredentials("", "")) }
|
||||
verifyNoMoreInteractions(mockLoginUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("WHEN login is called twice before finishing THEN use case is only called once")
|
||||
@Test
|
||||
fun onlyOneLoginIsSentOutWhenClickingRepeatedly() {
|
||||
runBlocking { whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Error(Throwable())) }
|
||||
|
||||
sut.onLogin()
|
||||
sut.onLogin()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
runBlocking { verify(mockLoginUseCase, times(1)).invoke(LoginCredentials("", "")) }
|
||||
verifyNoMoreInteractions(mockLoginUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN password and username WHEN login is called THEN proper credentials are used in usecase")
|
||||
@Test
|
||||
fun argumentsArePassedProperlyToLoginUseCase() {
|
||||
runBlocking {
|
||||
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Error(Throwable()))
|
||||
}
|
||||
sut.onPasswordChanged("pass")
|
||||
sut.onUsernameChanged("usr")
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onLogin()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
runBlocking {
|
||||
verify(mockLoginUseCase, times(1)).invoke(LoginCredentials("usr", "pass"))
|
||||
}
|
||||
verifyNoMoreInteractions(mockLoginUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN AnswerError WHEN login called THEN error is shown")
|
||||
@Test
|
||||
fun loginUnexpectedErrorResultsInErrorState() {
|
||||
runBlocking {
|
||||
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Error(Throwable()))
|
||||
}
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val errorTestObserver = sut.error.test()
|
||||
val navigateToHomeTestObserver = sut.navigateToHome.test()
|
||||
|
||||
sut.onLogin()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
loadingTestObserver.assertValueHistory(false, true, false)
|
||||
errorTestObserver.assertValueHistory(Event(AuthViewModel.ErrorType.GENERAL_NETWORK_ERROR))
|
||||
navigateToHomeTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@MethodSource("loginErrorStatusesArguments")
|
||||
@ParameterizedTest(name = "GIVEN answer success loginStatus {0} WHEN login called THEN error {1} is shown")
|
||||
fun invalidStatusResultsInErrorState(
|
||||
loginStatus: LoginStatus,
|
||||
errorType: AuthViewModel.ErrorType,
|
||||
) {
|
||||
runBlocking {
|
||||
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Success(loginStatus))
|
||||
}
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val errorTestObserver = sut.error.test()
|
||||
val navigateToHomeTestObserver = sut.navigateToHome.test()
|
||||
|
||||
sut.onLogin()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
loadingTestObserver.assertValueHistory(false, true, false)
|
||||
errorTestObserver.assertValueHistory(Event(errorType))
|
||||
navigateToHomeTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN answer success and login status success WHEN login called THEN navigation event is sent")
|
||||
@Test
|
||||
fun successLoginResultsInNavigation() {
|
||||
runBlocking {
|
||||
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Success(LoginStatus.SUCCESS))
|
||||
}
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val errorTestObserver = sut.error.test()
|
||||
val navigateToHomeTestObserver = sut.navigateToHome.test()
|
||||
|
||||
sut.onLogin()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
loadingTestObserver.assertValueHistory(false, true, false)
|
||||
errorTestObserver.assertNoValue()
|
||||
navigateToHomeTestObserver.assertValueHistory(Event(Unit))
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun loginErrorStatusesArguments(): Stream<Arguments?> = Stream.of(
|
||||
Arguments.of(LoginStatus.INVALID_CREDENTIALS, AuthViewModel.ErrorType.INVALID_CREDENTIALS),
|
||||
Arguments.of(LoginStatus.INVALID_PASSWORD, AuthViewModel.ErrorType.UNSUPPORTED_PASSWORD),
|
||||
Arguments.of(LoginStatus.INVALID_USERNAME, AuthViewModel.ErrorType.UNSUPPORTED_USERNAME)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
package org.fnives.test.showcase.hilt.ui.home
|
||||
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.fnives.test.showcase.android.testutil.InstantExecutorExtension
|
||||
import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher
|
||||
import org.fnives.test.showcase.hilt.core.content.AddContentToFavouriteUseCase
|
||||
import org.fnives.test.showcase.hilt.core.content.FetchContentUseCase
|
||||
import org.fnives.test.showcase.hilt.core.content.GetAllContentUseCase
|
||||
import org.fnives.test.showcase.hilt.core.content.RemoveContentFromFavouritesUseCase
|
||||
import org.fnives.test.showcase.hilt.core.login.LogoutUseCase
|
||||
import org.fnives.test.showcase.model.content.Content
|
||||
import org.fnives.test.showcase.model.content.ContentId
|
||||
import org.fnives.test.showcase.model.content.FavouriteContent
|
||||
import org.fnives.test.showcase.model.content.ImageUrl
|
||||
import org.fnives.test.showcase.model.shared.Resource
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.Mockito.verifyNoInteractions
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class)
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class MainViewModelTest {
|
||||
|
||||
private lateinit var sut: MainViewModel
|
||||
private lateinit var mockGetAllContentUseCase: GetAllContentUseCase
|
||||
private lateinit var mockLogoutUseCase: LogoutUseCase
|
||||
private lateinit var mockFetchContentUseCase: FetchContentUseCase
|
||||
private lateinit var mockAddContentToFavouriteUseCase: AddContentToFavouriteUseCase
|
||||
private lateinit var mockRemoveContentFromFavouritesUseCase: RemoveContentFromFavouritesUseCase
|
||||
private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockGetAllContentUseCase = mock()
|
||||
mockLogoutUseCase = mock()
|
||||
mockFetchContentUseCase = mock()
|
||||
mockAddContentToFavouriteUseCase = mock()
|
||||
mockRemoveContentFromFavouritesUseCase = mock()
|
||||
sut = MainViewModel(
|
||||
getAllContentUseCase = mockGetAllContentUseCase,
|
||||
logoutUseCase = mockLogoutUseCase,
|
||||
fetchContentUseCase = mockFetchContentUseCase,
|
||||
addContentToFavouriteUseCase = mockAddContentToFavouriteUseCase,
|
||||
removeContentFromFavouritesUseCase = mockRemoveContentFromFavouritesUseCase
|
||||
)
|
||||
}
|
||||
|
||||
@DisplayName("WHEN initialization THEN error false other states empty")
|
||||
@Test
|
||||
fun initialStateIsCorrect() {
|
||||
sut.errorMessage.test().assertValue(false)
|
||||
sut.content.test().assertNoValue()
|
||||
sut.loading.test().assertNoValue()
|
||||
sut.navigateToAuth.test().assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN initialized viewModel WHEN loading is returned THEN loading is shown")
|
||||
@Test
|
||||
fun loadingDataShowsInLoadingUIState() {
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading()))
|
||||
val errorMessageTestObserver = sut.errorMessage.test()
|
||||
val contentTestObserver = sut.content.test()
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val navigateToAuthTestObserver = sut.navigateToAuth.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
errorMessageTestObserver.assertValue(false)
|
||||
contentTestObserver.assertNoValue()
|
||||
loadingTestObserver.assertValue(true)
|
||||
navigateToAuthTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN loading then data WHEN observing content THEN proper states are shown")
|
||||
@Test
|
||||
fun loadingThenLoadedDataResultsInProperUIStates() {
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading(), Resource.Success(emptyList())))
|
||||
val errorMessageTestObserver = sut.errorMessage.test()
|
||||
val contentTestObserver = sut.content.test()
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val navigateToAuthTestObserver = sut.navigateToAuth.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
errorMessageTestObserver.assertValueHistory(false)
|
||||
contentTestObserver.assertValueHistory(listOf())
|
||||
loadingTestObserver.assertValueHistory(true, false)
|
||||
navigateToAuthTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN loading then error WHEN observing content THEN proper states are shown")
|
||||
@Test
|
||||
fun loadingThenErrorResultsInProperUIStates() {
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading(), Resource.Error(Throwable())))
|
||||
val errorMessageTestObserver = sut.errorMessage.test()
|
||||
val contentTestObserver = sut.content.test()
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val navigateToAuthTestObserver = sut.navigateToAuth.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
errorMessageTestObserver.assertValueHistory(false, true)
|
||||
contentTestObserver.assertValueHistory(emptyList())
|
||||
loadingTestObserver.assertValueHistory(true, false)
|
||||
navigateToAuthTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN loading then error then loading then data WHEN observing content THEN proper states are shown")
|
||||
@Test
|
||||
fun loadingThenErrorThenLoadingThenDataResultsInProperUIStates() {
|
||||
val content = listOf(
|
||||
FavouriteContent(Content(ContentId(""), "", "", ImageUrl("")), false)
|
||||
)
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(
|
||||
flowOf(
|
||||
Resource.Loading(),
|
||||
Resource.Error(Throwable()),
|
||||
Resource.Loading(),
|
||||
Resource.Success(content)
|
||||
)
|
||||
)
|
||||
val errorMessageTestObserver = sut.errorMessage.test()
|
||||
val contentTestObserver = sut.content.test()
|
||||
val loadingTestObserver = sut.loading.test()
|
||||
val navigateToAuthTestObserver = sut.navigateToAuth.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
errorMessageTestObserver.assertValueHistory(false, true, false)
|
||||
contentTestObserver.assertValueHistory(emptyList(), content)
|
||||
loadingTestObserver.assertValueHistory(true, false, true, false)
|
||||
navigateToAuthTestObserver.assertNoValue()
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN loading viewModel WHEN refreshing THEN usecase is not called")
|
||||
@Test
|
||||
fun fetchIsIgnoredIfViewModelIsStillLoading() {
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading()))
|
||||
sut.content.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onRefresh()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
verifyNoInteractions(mockFetchContentUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN non loading viewModel WHEN refreshing THEN usecase is called")
|
||||
@Test
|
||||
fun fetchIsCalledIfViewModelIsLoaded() {
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf())
|
||||
sut.content.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onRefresh()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
verify(mockFetchContentUseCase, times(1)).invoke()
|
||||
verifyNoMoreInteractions(mockFetchContentUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN loading viewModel WHEN loging out THEN usecase is called")
|
||||
@Test
|
||||
fun loadingViewModelStillCalsLogout() {
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading()))
|
||||
sut.content.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onLogout()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
runBlocking { verify(mockLogoutUseCase, times(1)).invoke() }
|
||||
verifyNoMoreInteractions(mockLogoutUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN non loading viewModel WHEN loging out THEN usecase is called")
|
||||
@Test
|
||||
fun nonLoadingViewModelStillCalsLogout() {
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf())
|
||||
sut.content.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onLogout()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
runBlocking { verify(mockLogoutUseCase, times(1)).invoke() }
|
||||
verifyNoMoreInteractions(mockLogoutUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN success content list viewModel WHEN toggling a nonexistent contentId THEN nothing happens")
|
||||
@Test
|
||||
fun interactionWithNonExistentContentIdIsIgnored() {
|
||||
val contents = listOf(
|
||||
FavouriteContent(Content(ContentId("a"), "", "", ImageUrl("")), false),
|
||||
FavouriteContent(Content(ContentId("b"), "", "", ImageUrl("")), true)
|
||||
)
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Success(contents)))
|
||||
sut.content.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onFavouriteToggleClicked(ContentId("c"))
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
verifyNoInteractions(mockRemoveContentFromFavouritesUseCase)
|
||||
verifyNoInteractions(mockAddContentToFavouriteUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN success content list viewModel WHEN toggling a favourite contentId THEN remove favourite usecase is called")
|
||||
@Test
|
||||
fun togglingFavouriteContentCallsRemoveFromFavourite() {
|
||||
val contents = listOf(
|
||||
FavouriteContent(Content(ContentId("a"), "", "", ImageUrl("")), false),
|
||||
FavouriteContent(Content(ContentId("b"), "", "", ImageUrl("")), true)
|
||||
)
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Success(contents)))
|
||||
sut.content.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onFavouriteToggleClicked(ContentId("b"))
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
runBlocking { verify(mockRemoveContentFromFavouritesUseCase, times(1)).invoke(ContentId("b")) }
|
||||
verifyNoMoreInteractions(mockRemoveContentFromFavouritesUseCase)
|
||||
verifyNoInteractions(mockAddContentToFavouriteUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN success content list viewModel WHEN toggling a not favourite contentId THEN add favourite usecase is called")
|
||||
@Test
|
||||
fun togglingNonFavouriteContentCallsAddToFavourite() {
|
||||
val contents = listOf(
|
||||
FavouriteContent(Content(ContentId("a"), "", "", ImageUrl("")), false),
|
||||
FavouriteContent(Content(ContentId("b"), "", "", ImageUrl("")), true)
|
||||
)
|
||||
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Success(contents)))
|
||||
sut.content.test()
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
sut.onFavouriteToggleClicked(ContentId("a"))
|
||||
testScheduler.advanceUntilIdle()
|
||||
|
||||
verifyNoInteractions(mockRemoveContentFromFavouritesUseCase)
|
||||
runBlocking { verify(mockAddContentToFavouriteUseCase, times(1)).invoke(ContentId("a")) }
|
||||
verifyNoMoreInteractions(mockAddContentToFavouriteUseCase)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package org.fnives.test.showcase.hilt.ui.shared
|
||||
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("TestFunctionName")
|
||||
internal class EventTest {
|
||||
|
||||
@DisplayName("GIVEN event WHEN consumed is called THEN value is returned")
|
||||
@Test
|
||||
fun consumedReturnsValue() {
|
||||
val expected = "a"
|
||||
|
||||
val actual = Event("a").consume()
|
||||
|
||||
Assertions.assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN consumed event WHEN consumed is called THEN null is returned")
|
||||
@Test
|
||||
fun consumedEventReturnsNull() {
|
||||
val expected: String? = null
|
||||
val event = Event("a")
|
||||
event.consume()
|
||||
|
||||
val actual = event.consume()
|
||||
|
||||
Assertions.assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN event WHEN peek is called THEN value is returned")
|
||||
@Test
|
||||
fun peekReturnsValue() {
|
||||
val expected = "a"
|
||||
|
||||
val actual = Event("a").peek()
|
||||
|
||||
Assertions.assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN consumed event WHEN peek is called THEN value is returned")
|
||||
@Test
|
||||
fun consumedEventPeekedReturnsValue() {
|
||||
val expected = "a"
|
||||
val event = Event("a")
|
||||
event.consume()
|
||||
|
||||
val actual = event.peek()
|
||||
|
||||
Assertions.assertEquals(expected, actual)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package org.fnives.test.showcase.hilt.ui.splash
|
||||
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import org.fnives.test.showcase.android.testutil.InstantExecutorExtension
|
||||
import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher
|
||||
import org.fnives.test.showcase.hilt.core.login.IsUserLoggedInUseCase
|
||||
import org.fnives.test.showcase.hilt.ui.shared.Event
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.whenever
|
||||
|
||||
@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class)
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class SplashViewModelTest {
|
||||
|
||||
private lateinit var mockIsUserLoggedInUseCase: IsUserLoggedInUseCase
|
||||
private lateinit var sut: SplashViewModel
|
||||
private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockIsUserLoggedInUseCase = mock()
|
||||
sut = SplashViewModel(mockIsUserLoggedInUseCase)
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN not logged in user WHEN splash started THEN after half a second navigated to authentication")
|
||||
@Test
|
||||
fun loggedOutUserGoesToAuthentication() {
|
||||
whenever(mockIsUserLoggedInUseCase.invoke()).doReturn(false)
|
||||
val navigateToTestObserver = sut.navigateTo.test()
|
||||
|
||||
testScheduler.advanceTimeBy(501)
|
||||
|
||||
navigateToTestObserver.assertValueHistory(Event(SplashViewModel.NavigateTo.AUTHENTICATION))
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN logged in user WHEN splash started THEN after half a second navigated to home")
|
||||
@Test
|
||||
fun loggedInUserGoesToHome() {
|
||||
whenever(mockIsUserLoggedInUseCase.invoke()).doReturn(true)
|
||||
val navigateToTestObserver = sut.navigateTo.test()
|
||||
|
||||
testScheduler.advanceTimeBy(501)
|
||||
|
||||
navigateToTestObserver.assertValueHistory(Event(SplashViewModel.NavigateTo.HOME))
|
||||
}
|
||||
|
||||
@DisplayName("GIVEN not logged in user WHEN splash started THEN before half a second no event is sent")
|
||||
@Test
|
||||
fun withoutEnoughTimeNoNavigationHappens() {
|
||||
whenever(mockIsUserLoggedInUseCase.invoke()).doReturn(false)
|
||||
val navigateToTestObserver = sut.navigateTo.test()
|
||||
|
||||
testScheduler.advanceTimeBy(500)
|
||||
|
||||
navigateToTestObserver.assertNoValue()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
mock-maker-inline
|
||||
3
hilt/hilt-app/src/test/resources/robolectric.properties
Normal file
3
hilt/hilt-app/src/test/resources/robolectric.properties
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
sdk=22,28
|
||||
instrumentedPackages=androidx.loader.content
|
||||
application = dagger.hilt.android.testing.HiltTestApplication
|
||||
Loading…
Add table
Add a link
Reference in a new issue