Merge pull request #32 from fknives/issue#6-displayname

Issue#6 displayname
This commit is contained in:
Gergely Hegedis 2022-01-04 18:10:59 +02:00 committed by GitHub
commit e34ee60df5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 384 additions and 175 deletions

View file

@ -7,12 +7,12 @@ plugins {
}
android {
compileSdkVersion 31
compileSdk 31
defaultConfig {
applicationId "org.fnives.test.showcase"
minSdkVersion 21
targetSdkVersion 31
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
buildConfigField "String", "BASE_URL", '"https://606844a10add49001733fe6b.mockapi.io/"'
@ -138,7 +138,6 @@ dependencies {
testImplementation "androidx.test.espresso:espresso-core:$testing_espresso_version"
testImplementation "androidx.test.espresso:espresso-intents:$testing_espresso_version"
testImplementation project(':mockserver')
testImplementation "com.jakewharton.espresso:okhttp3-idling-resource:$testing_okhttp3_idling_resource_version"
testImplementation "androidx.arch.core:core-testing:$testing_androidx_arch_core_version"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version"
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
@ -153,10 +152,19 @@ dependencies {
androidTestImplementation "androidx.test.espresso:espresso-core:$testing_espresso_version"
androidTestImplementation "androidx.test.espresso:espresso-intents:$testing_espresso_version"
androidTestImplementation project(':mockserver')
androidTestImplementation "com.jakewharton.espresso:okhttp3-idling-resource:$testing_okhttp3_idling_resource_version"
androidTestImplementation "androidx.arch.core:core-testing:$testing_androidx_arch_core_version"
androidTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version"
androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
kaptAndroidTest "com.google.dagger:hilt-compiler:$hilt_version"
androidTestImplementation project(":network") // hilt needs it
implementation "io.reactivex.rxjava3:rxjava:3.1.2"
}
///Users/gergelyhegedus/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk8/1.5.31/ff5d99aecd328872494e8921b72bf6e3af97af3e/kotlin-stdlib-jdk8-1.5.31.jar (version 1.5)
///Users/gergelyhegedus/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-jdk7/1.5.31/77e0f2568912e45d26c31fd417a332458508acdf/kotlin-stdlib-jdk7-1.5.31.jar (version 1.5)
///Users/gergelyhegedus/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib/1.6.0/a40b8b22529b733892edf4b73468ce598bb17f04/kotlin-stdlib-1.6.0.jar (version 1.6)
///Users/gergelyhegedus/.gradle/caches/modules-2/files-2.1/org.jetbrains.kotlin/kotlin-stdlib-common/1.6.0/7857e365f925cfa060f941c1357cda1f8790502c/kotlin-stdlib-common-1.6.0.jar (version 1.6)
//w: Some runtime JAR files in the classpath have an incompatible version. Consider removing them from the classpath
//dagger.lint.DaggerIssueRegistry in /Users/gergelyhegedus/.gradle/caches/transforms-3/7d8d9a87fed97b25e3e147795231ede4/transformed/jetified-dagger-lint-aar-2.40/jars/lint.jar does not specify a vendor; see IssueRegistry#vendor

View file

@ -3,6 +3,7 @@ package org.fnives.test.showcase.ui.home
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.liveData
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
@ -52,7 +53,7 @@ class MainViewModel @Inject constructor(
}
val content: LiveData<List<FavouriteContent>> = _content
private val _errorMessage = MutableLiveData<Boolean>(false)
val errorMessage: LiveData<Boolean> = _errorMessage
val errorMessage: LiveData<Boolean> = _errorMessage.distinctUntilChanged()
private val _navigateToAuth = MutableLiveData<Event<Unit>>()
val navigateToAuth: LiveData<Event<Unit>> = _navigateToAuth

View file

@ -40,8 +40,9 @@ internal class FavouriteContentLocalStorageImplTest {
hiltRule.inject()
}
/** GIVEN content_id WHEN added to Favourite THEN it can be read out */
@Test
fun GIVEN_content_id_WHEN_added_to_Favourite_THEN_it_can_be_read_out() = runBlocking {
fun addingContentIdToFavouriteCanBeLaterReadOut() = runBlocking {
val expected = listOf(ContentId("a"))
sut.markAsFavourite(ContentId("a"))
@ -50,8 +51,9 @@ internal class FavouriteContentLocalStorageImplTest {
Assert.assertEquals(expected, actual)
}
/** GIVEN content_id added WHEN removed to Favourite THEN it no longer can be read out */
@Test
fun GIVEN_content_id_added_WHEN_removed_to_Favourite_THEN_it_no_longer_can_be_read_out() =
fun contentIdAddedThenRemovedCanNoLongerBeReadOut() =
runBlocking {
val expected = listOf<ContentId>()
sut.markAsFavourite(ContentId("b"))
@ -62,8 +64,9 @@ internal class FavouriteContentLocalStorageImplTest {
Assert.assertEquals(expected, actual)
}
/** GIVEN empty database WHILE observing content WHEN favourite added THEN change is emitted */
@Test
fun GIVEN_empty_database_WHILE_observing_content_WHEN_favourite_added_THEN_change_is_emitted() =
fun addingFavouriteUpdatesExistingObservers() =
runBlocking<Unit> {
val expected = listOf(listOf(), listOf(ContentId("a")))
@ -78,8 +81,9 @@ internal class FavouriteContentLocalStorageImplTest {
Assert.assertEquals(expected, actual.await())
}
/** GIVEN non empty database WHILE observing content WHEN favourite removed THEN change is emitted */
@Test
fun GIVEN_non_empty_database_WHILE_observing_content_WHEN_favourite_removed_THEN_change_is_emitted() =
fun removingFavouriteUpdatesExistingObservers() =
runBlocking<Unit> {
val expected = listOf(listOf(ContentId("a")), listOf())
sut.markAsFavourite(ContentId("a"))

View file

@ -39,8 +39,9 @@ internal class FavouriteContentLocalStorageImplTest : KoinTest {
stopKoin()
}
/** GIVEN content_id WHEN added to Favourite THEN it can be read out */
@Test
fun GIVEN_content_id_WHEN_added_to_Favourite_THEN_it_can_be_read_out() = runBlocking {
fun addingContentIdToFavouriteCanBeLaterReadOut() = runBlocking {
val expected = listOf(ContentId("a"))
sut.markAsFavourite(ContentId("a"))
@ -49,8 +50,9 @@ internal class FavouriteContentLocalStorageImplTest : KoinTest {
Assert.assertEquals(expected, actual)
}
/** GIVEN content_id added WHEN removed to Favourite THEN it no longer can be read out */
@Test
fun GIVEN_content_id_added_WHEN_removed_to_Favourite_THEN_it_no_longer_can_be_read_out() =
fun contentIdAddedThenRemovedCanNoLongerBeReadOut() =
runBlocking {
val expected = listOf<ContentId>()
sut.markAsFavourite(ContentId("b"))
@ -61,8 +63,9 @@ internal class FavouriteContentLocalStorageImplTest : KoinTest {
Assert.assertEquals(expected, actual)
}
/** GIVEN empty database WHILE observing content WHEN favourite added THEN change is emitted */
@Test
fun GIVEN_empty_database_WHILE_observing_content_WHEN_favourite_added_THEN_change_is_emitted() =
fun addingFavouriteUpdatesExistingObservers() =
runBlocking<Unit> {
val expected = listOf(listOf(), listOf(ContentId("a")))
@ -77,8 +80,9 @@ internal class FavouriteContentLocalStorageImplTest : KoinTest {
Assert.assertEquals(expected, actual.await())
}
/** GIVEN non empty database WHILE observing content WHEN favourite removed THEN change is emitted */
@Test
fun GIVEN_non_empty_database_WHILE_observing_content_WHEN_favourite_removed_THEN_change_is_emitted() =
fun removingFavouriteUpdatesExistingObservers() =
runBlocking<Unit> {
val expected = listOf(listOf(ContentId("a")), listOf())
sut.markAsFavourite(ContentId("a"))

View file

@ -0,0 +1,46 @@
package org.fnives.test.showcase.testutils.idling
import androidx.annotation.CheckResult
import androidx.annotation.NonNull
import androidx.test.espresso.IdlingResource
import okhttp3.Dispatcher
import okhttp3.OkHttpClient
/**
* AndroidX version of Jake Wharton's OkHttp3IdlingResource.
*
* Reference: https://github.com/JakeWharton/okhttp-idling-resource/blob/master/src/main/java/com/jakewharton/espresso/OkHttp3IdlingResource.java
*/
class OkHttp3IdlingResource private constructor(
private val name: String,
private val dispatcher: Dispatcher
) : IdlingResource {
@Volatile
var callback: IdlingResource.ResourceCallback? = null
init {
dispatcher.idleCallback = Runnable { callback?.onTransitionToIdle() }
}
override fun getName(): String = name
override fun isIdleNow(): Boolean = dispatcher.runningCallsCount() == 0
override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback?) {
this.callback = callback
}
companion object {
/**
* Create a new [IdlingResource] from `client` as `name`. You must register
* this instance using `Espresso.registerIdlingResources`.
*/
@CheckResult
@NonNull // Extra guards as a library.
fun create(@NonNull name: String?, @NonNull client: OkHttpClient?): OkHttp3IdlingResource {
if (name == null) throw NullPointerException("name == null")
if (client == null) throw NullPointerException("client == null")
return OkHttp3IdlingResource(name, client.dispatcher)
}
}
}

View file

@ -2,7 +2,6 @@ package org.fnives.test.showcase.testutils.idling
import androidx.annotation.CheckResult
import androidx.test.espresso.IdlingResource
import com.jakewharton.espresso.OkHttp3IdlingResource
import okhttp3.OkHttpClient
import org.fnives.test.showcase.hilt.SessionLessQualifier
import org.fnives.test.showcase.hilt.SessionQualifier

View file

@ -82,8 +82,9 @@ class MainActivityTest {
disposable.dispose()
}
/** GIVEN initialized MainActivity WHEN signout is clicked THEN user is signed out */
@Test
fun GIVEN_initialized_MainActivity_WHEN_signout_is_clicked_THEN_user_is_signed_out() {
fun signOutClickedResultsInNavigation() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Error(false))
activityScenario = ActivityScenario.launch(HiltMainActivity::class.java)
@ -96,8 +97,9 @@ class MainActivityTest {
Assert.assertEquals(false, setupLoggedInState.isLoggedIn())
}
/** GIVEN success response WHEN data is returned THEN it is shown on the ui */
@Test
fun GIVEN_success_response_WHEN_data_is_returned_THEN_it_is_shown_on_the_ui() {
fun successfulDataLoadingShowsTheElementsOnTheUI() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(HiltMainActivity::class.java)
@ -109,8 +111,9 @@ class MainActivityTest {
homeRobot.assertDidNotNavigateToAuth()
}
/** GIVEN success response WHEN item is clicked THEN ui is updated */
@Test
fun GIVEN_success_response_WHEN_item_is_clicked_THEN_ui_is_updated() {
fun clickingOnListElementUpdatesTheElementsFavouriteState() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(HiltMainActivity::class.java)
@ -124,8 +127,9 @@ class MainActivityTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN success response WHEN item is clicked THEN ui is updated even if activity is recreated */
@Test
fun GIVEN_success_response_WHEN_item_is_clicked_THEN_ui_is_updated_even_if_activity_is_recreated() {
fun elementFavouritedIsKeptEvenIfActivityIsRecreated() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(HiltMainActivity::class.java)
@ -144,8 +148,9 @@ class MainActivityTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN success response WHEN item is clicked then clicked again THEN ui is updated */
@Test
fun GIVEN_success_response_WHEN_item_is_clicked_then_clicked_again_THEN_ui_is_updated() {
fun clickingAnElementMultipleTimesProperlyUpdatesIt() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(HiltMainActivity::class.java)
@ -161,8 +166,9 @@ class MainActivityTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN error response WHEN loaded THEN error is Shown */
@Test
fun GIVEN_error_response_WHEN_loaded_THEN_error_is_Shown() {
fun networkErrorResultsInUIErrorStateShown() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Error(false))
activityScenario = ActivityScenario.launch(HiltMainActivity::class.java)
@ -173,8 +179,9 @@ class MainActivityTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN error response then success WHEN retried THEN success is shown */
@Test
fun GIVEN_error_response_then_success_WHEN_retried_THEN_success_is_shown() {
fun retryingFromErrorStateAndSucceedingShowsTheData() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(
ContentScenario.Error(false)
@ -193,8 +200,9 @@ class MainActivityTest {
homeRobot.assertDidNotNavigateToAuth()
}
/** GIVEN success then error WHEN retried THEN error is shown */
@Test
fun GIVEN_success_then_error_WHEN_retried_THEN_error_is_shown() {
fun errorIsShownIfTheDataIsFetchedAndErrorIsReceived() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(
ContentScenario.Success(false)
@ -215,8 +223,9 @@ class MainActivityTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN unauthenticated then success WHEN loaded THEN success is shown */
@Test
fun GIVEN_unauthenticated_then_success_WHEN_loaded_THEN_success_is_shown() {
fun authenticationIsHandledWithASingleLoading() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(
ContentScenario.Unauthorized(false)
@ -233,8 +242,9 @@ class MainActivityTest {
homeRobot.assertDidNotNavigateToAuth()
}
/** GIVEN unauthenticated then error WHEN loaded THEN navigated to auth */
@Test
fun GIVEN_unauthenticated_then_error_WHEN_loaded_THEN_navigated_to_auth() {
fun sessionExpirationResultsInNavigation() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Unauthorized(false))
.setScenario(RefreshTokenScenario.Error)

View file

@ -72,8 +72,9 @@ class AuthActivityTest {
disposable.dispose()
}
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
@Test
fun GIVEN_non_empty_password_and_username_and_successful_response_WHEN_signIn_THEN_no_error_is_shown_and_navigating_to_home() {
fun properLoginResultsInNavigationToHome() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup.setScenario(
AuthScenario.Success(
password = "alma",
@ -93,8 +94,9 @@ class AuthActivityTest {
loginRobot.assertNavigatedToHome()
}
/** GIVEN empty password and username WHEN signIn THEN error password is shown */
@Test
fun GIVEN_empty_password_and_username_WHEN_signIn_THEN_error_password_is_shown() {
fun emptyPasswordShowsProperErrorMessage() {
activityScenario = ActivityScenario.launch(HiltAuthActivity::class.java)
loginRobot
.setUsername("banan")
@ -108,8 +110,9 @@ class AuthActivityTest {
.assertNotLoading()
}
/** GIVEN password and empty username WHEN signIn THEN error username is shown */
@Test
fun GIVEN_password_and_empty_username_WHEN_signIn_THEN_error_username_is_shown() {
fun emptyUserNameShowsProperErrorMessage() {
activityScenario = ActivityScenario.launch(HiltAuthActivity::class.java)
loginRobot
.setPassword("banan")
@ -123,8 +126,9 @@ class AuthActivityTest {
.assertNotLoading()
}
/** GIVEN password and username and invalid credentials response WHEN signIn THEN error invalid credentials is shown */
@Test
fun GIVEN_password_and_username_and_invalid_credentials_response_WHEN_signIn_THEN_error_invalid_credentials_is_shown() {
fun invalidCredentialsGivenShowsProperErrorMessage() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup.setScenario(
AuthScenario.InvalidCredentials(username = "alma", password = "banan")
)
@ -143,8 +147,9 @@ class AuthActivityTest {
.assertNotLoading()
}
/** GIVEN password and username and error response WHEN signIn THEN error invalid credentials is shown */
@Test
fun GIVEN_password_and_username_and_error_response_WHEN_signIn_THEN_error_invalid_credentials_is_shown() {
fun networkErrorShowsProperErrorMessage() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup.setScenario(
AuthScenario.GenericError(username = "alma", password = "banan")
)

View file

@ -71,8 +71,9 @@ class SplashActivityTest : KoinTest {
disposable?.dispose()
}
/** GIVEN loggedInState WHEN opened THEN MainActivity is started */
@Test
fun GIVEN_loggedInState_WHEN_opened_THEN_MainActivity_is_started() {
fun loggedInStateNavigatesToHome() {
setupLoggedInState.setupLogin(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)
@ -85,8 +86,9 @@ class SplashActivityTest : KoinTest {
setupLoggedInState.setupLogout()
}
/** GIVEN loggedOffState WHEN opened THEN AuthActivity is started */
@Test
fun GIVEN_loggedOffState_WHEN_opened_THEN_AuthActivity_is_started() {
fun loggedOutStatesNavigatesToAuthentication() {
setupLoggedInState.setupLogout()
activityScenario = ActivityScenario.launch(HiltSplashActivity::class.java)

View file

@ -2,7 +2,6 @@ package org.fnives.test.showcase.testutils.idling
import androidx.annotation.CheckResult
import androidx.test.espresso.IdlingResource
import com.jakewharton.espresso.OkHttp3IdlingResource
import okhttp3.OkHttpClient
import org.koin.core.qualifier.StringQualifier
import org.koin.test.KoinTest

View file

@ -37,7 +37,8 @@ class MainActivityTest : KoinTest {
@Rule
@JvmField
val snackbarVerificationTestRule = SpecificTestConfigurationsFactory.createSnackbarVerification()
val snackbarVerificationTestRule =
SpecificTestConfigurationsFactory.createSnackbarVerification()
@Rule
@JvmField
@ -73,8 +74,9 @@ class MainActivityTest : KoinTest {
disposable.dispose()
}
/** GIVEN initialized MainActivity WHEN signout is clicked THEN user is signed out */
@Test
fun GIVEN_initialized_MainActivity_WHEN_signout_is_clicked_THEN_user_is_signed_out() {
fun signOutClickedResultsInNavigation() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Error(false))
activityScenario = ActivityScenario.launch(MainActivity::class.java)
@ -87,8 +89,9 @@ class MainActivityTest : KoinTest {
Assert.assertEquals(false, SetupLoggedInState.isLoggedIn())
}
/** GIVEN success response WHEN data is returned THEN it is shown on the ui */
@Test
fun GIVEN_success_response_WHEN_data_is_returned_THEN_it_is_shown_on_the_ui() {
fun successfulDataLoadingShowsTheElementsOnTheUI() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(MainActivity::class.java)
@ -100,8 +103,9 @@ class MainActivityTest : KoinTest {
homeRobot.assertDidNotNavigateToAuth()
}
/** GIVEN success response WHEN item is clicked THEN ui is updated */
@Test
fun GIVEN_success_response_WHEN_item_is_clicked_THEN_ui_is_updated() {
fun clickingOnListElementUpdatesTheElementsFavouriteState() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(MainActivity::class.java)
@ -115,8 +119,9 @@ class MainActivityTest : KoinTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN success response WHEN item is clicked THEN ui is updated even if activity is recreated */
@Test
fun GIVEN_success_response_WHEN_item_is_clicked_THEN_ui_is_updated_even_if_activity_is_recreated() {
fun elementFavouritedIsKeptEvenIfActivityIsRecreated() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(MainActivity::class.java)
@ -135,8 +140,9 @@ class MainActivityTest : KoinTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN success response WHEN item is clicked then clicked again THEN ui is updated */
@Test
fun GIVEN_success_response_WHEN_item_is_clicked_then_clicked_again_THEN_ui_is_updated() {
fun clickingAnElementMultipleTimesProperlyUpdatesIt() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Success(false))
activityScenario = ActivityScenario.launch(MainActivity::class.java)
@ -152,8 +158,9 @@ class MainActivityTest : KoinTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN error response WHEN loaded THEN error is Shown */
@Test
fun GIVEN_error_response_WHEN_loaded_THEN_error_is_Shown() {
fun networkErrorResultsInUIErrorStateShown() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Error(false))
activityScenario = ActivityScenario.launch(MainActivity::class.java)
@ -164,8 +171,9 @@ class MainActivityTest : KoinTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN error response then success WHEN retried THEN success is shown */
@Test
fun GIVEN_error_response_then_success_WHEN_retried_THEN_success_is_shown() {
fun retryingFromErrorStateAndSucceedingShowsTheData() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(
ContentScenario.Error(false)
@ -184,8 +192,9 @@ class MainActivityTest : KoinTest {
homeRobot.assertDidNotNavigateToAuth()
}
/** GIVEN success then error WHEN retried THEN error is shown */
@Test
fun GIVEN_success_then_error_WHEN_retried_THEN_error_is_shown() {
fun errorIsShownIfTheDataIsFetchedAndErrorIsReceived() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(
ContentScenario.Success(false)
@ -206,8 +215,9 @@ class MainActivityTest : KoinTest {
.assertDidNotNavigateToAuth()
}
/** GIVEN unauthenticated then success WHEN loaded THEN success is shown */
@Test
fun GIVEN_unauthenticated_then_success_WHEN_loaded_THEN_success_is_shown() {
fun authenticationIsHandledWithASingleLoading() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(
ContentScenario.Unauthorized(false)
@ -224,8 +234,9 @@ class MainActivityTest : KoinTest {
homeRobot.assertDidNotNavigateToAuth()
}
/** GIVEN unauthenticated then error WHEN loaded THEN navigated to auth */
@Test
fun GIVEN_unauthenticated_then_error_WHEN_loaded_THEN_navigated_to_auth() {
fun sessionExpirationResultsInNavigation() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup
.setScenario(ContentScenario.Unauthorized(false))
.setScenario(RefreshTokenScenario.Error)

View file

@ -66,8 +66,9 @@ class AuthActivityTest : KoinTest {
disposable.dispose()
}
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
@Test
fun GIVEN_non_empty_password_and_username_and_successful_response_WHEN_signIn_THEN_no_error_is_shown_and_navigating_to_home() {
fun properLoginResultsInNavigationToHome() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup.setScenario(
AuthScenario.Success(
password = "alma",
@ -87,8 +88,9 @@ class AuthActivityTest : KoinTest {
loginRobot.assertNavigatedToHome()
}
/** GIVEN empty password and username WHEN signIn THEN error password is shown */
@Test
fun GIVEN_empty_password_and_username_WHEN_signIn_THEN_error_password_is_shown() {
fun emptyPasswordShowsProperErrorMessage() {
activityScenario = ActivityScenario.launch(AuthActivity::class.java)
loginRobot
.setUsername("banan")
@ -102,8 +104,9 @@ class AuthActivityTest : KoinTest {
.assertNotLoading()
}
/** GIVEN password and empty username WHEN signIn THEN error username is shown */
@Test
fun GIVEN_password_and_empty_username_WHEN_signIn_THEN_error_username_is_shown() {
fun emptyUserNameShowsProperErrorMessage() {
activityScenario = ActivityScenario.launch(AuthActivity::class.java)
loginRobot
.setPassword("banan")
@ -117,8 +120,9 @@ class AuthActivityTest : KoinTest {
.assertNotLoading()
}
/** GIVEN password and username and invalid credentials response WHEN signIn THEN error invalid credentials is shown */
@Test
fun GIVEN_password_and_username_and_invalid_credentials_response_WHEN_signIn_THEN_error_invalid_credentials_is_shown() {
fun invalidCredentialsGivenShowsProperErrorMessage() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup.setScenario(
AuthScenario.InvalidCredentials(username = "alma", password = "banan")
)
@ -137,8 +141,9 @@ class AuthActivityTest : KoinTest {
.assertNotLoading()
}
/** GIVEN password and username and error response WHEN signIn THEN error invalid credentials is shown */
@Test
fun GIVEN_password_and_username_and_error_response_WHEN_signIn_THEN_error_invalid_credentials_is_shown() {
fun networkErrorShowsProperErrorMessage() {
mockServerScenarioSetupTestRule.mockServerScenarioSetup.setScenario(
AuthScenario.GenericError(username = "alma", password = "banan")
)

View file

@ -61,8 +61,9 @@ class SplashActivityTest : KoinTest {
disposable.dispose()
}
/** GIVEN loggedInState WHEN opened THEN MainActivity is started */
@Test
fun GIVEN_loggedInState_WHEN_opened_THEN_MainActivity_is_started() {
fun loggedInStateNavigatesToHome() {
SetupLoggedInState.setupLogin(mockServerScenarioSetupTestRule.mockServerScenarioSetup)
activityScenario = ActivityScenario.launch(SplashActivity::class.java)
@ -75,8 +76,9 @@ class SplashActivityTest : KoinTest {
SetupLoggedInState.setupLogout()
}
/** GIVEN loggedOffState WHEN opened THEN AuthActivity is started */
@Test
fun GIVEN_loggedOffState_WHEN_opened_THEN_AuthActivity_is_started() {
fun loggedOutStatesNavigatesToAuthentication() {
SetupLoggedInState.setupLogout()
activityScenario = ActivityScenario.launch(SplashActivity::class.java)

View file

@ -6,6 +6,14 @@ import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeEachCallback
import org.junit.jupiter.api.extension.ExtensionContext
/**
* Junit5 Version of InstantTaskExecutorRule from Junit4
*
* reference: https://developer.android.com/reference/androidx/arch/core/executor/testing/InstantTaskExecutorRule
*
* A JUnit5 Extensions that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously.
* You can use this extension for your host side tests that use Architecture Components.
*/
class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {

View file

@ -5,10 +5,16 @@ import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.fnives.test.showcase.storage.database.DatabaseInitialization
import org.fnives.test.showcase.testutils.TestMainDispatcher.Companion.testDispatcher
import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeEachCallback
import org.junit.jupiter.api.extension.ExtensionContext
/**
* Custom Junit5 Extension which replaces the [DatabaseInitialization]'s dispatcher and main dispatcher with a [TestCoroutineDispatcher]
*
* One can access the test dispatcher via [testDispatcher] static getter.
*/
class TestMainDispatcher : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {

View file

@ -39,7 +39,7 @@ internal class AuthViewModelTest {
sut = AuthViewModel(mockLoginUseCase)
}
@DisplayName("GIVEN_initialized_viewModel_WHEN_observed_THEN_loading_false_other_fields_are_empty")
@DisplayName("GIVEN initialized viewModel WHEN observed THEN loading false other fields are empty")
@Test
fun initialSetup() {
testDispatcher.resumeDispatcher()
@ -51,7 +51,7 @@ internal class AuthViewModelTest {
sut.navigateToHome.test().assertNoValue()
}
@DisplayName("GIVEN_password_text_WHEN_onPasswordChanged_is_called_THEN_password_livedata_is_updated")
@DisplayName("GIVEN password text WHEN onPasswordChanged is called THEN password livedata is updated")
@Test
fun whenPasswordChangedLiveDataIsUpdated() {
testDispatcher.resumeDispatcher()
@ -67,7 +67,7 @@ internal class AuthViewModelTest {
sut.navigateToHome.test().assertNoValue()
}
@DisplayName("GIVEN_username_text_WHEN_onUsernameChanged_is_called_THEN_username_livedata_is_updated")
@DisplayName("GIVEN username text WHEN onUsernameChanged is called THEN username livedata is updated")
@Test
fun whenUsernameChangedLiveDataIsUpdated() {
testDispatcher.resumeDispatcher()
@ -83,7 +83,7 @@ internal class AuthViewModelTest {
sut.navigateToHome.test().assertNoValue()
}
@DisplayName("GIVEN_no_password_or_username_WHEN_login_is_Called_THEN_empty_credentials_are_used_in_usecase")
@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()
@ -99,8 +99,9 @@ internal class AuthViewModelTest {
verifyNoMoreInteractions(mockLoginUseCase)
}
@DisplayName("WHEN login is called twice before finishing THEN use case is only called once")
@Test
fun WHEN_login_is_Called_twice_THEN_use_case_is_only_called_once() {
fun onlyOneLoginIsSentOutWhenClickingRepeatedly() {
runBlocking { whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Error(Throwable())) }
sut.onLogin()
@ -111,8 +112,9 @@ internal class AuthViewModelTest {
verifyNoMoreInteractions(mockLoginUseCase)
}
@DisplayName("GIVEN password and username WHEN login is called THEN proper credentials are used in usecase")
@Test
fun GIVEN_password_and_username_WHEN_login_is_Called_THEN_not_empty_credentials_are_used_in_usecase() {
fun argumentsArePassedProperlyToLoginUseCase() {
runBlocking {
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Error(Throwable()))
}
@ -128,8 +130,9 @@ internal class AuthViewModelTest {
verifyNoMoreInteractions(mockLoginUseCase)
}
@DisplayName("GIVEN AnswerError WHEN login called THEN error is shown")
@Test
fun GIVEN_answer_error_WHEN_login_called_THEN_error_is_shown() {
fun loginErrorResultsInErrorState() {
runBlocking {
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Error(Throwable()))
}
@ -146,8 +149,8 @@ internal class AuthViewModelTest {
}
@MethodSource("loginErrorStatusesArguments")
@ParameterizedTest(name = "GIVEN_answer_success_loginStatus_{0}_WHEN_login_called_THEN_error_{1}_is_shown")
fun GIVEN_answer_success_invalid_loginStatus_WHEN_login_called_THEN_error_is_shown(
@ParameterizedTest(name = "GIVEN answer success loginStatus {0} WHEN login called THEN error {1} is shown")
fun invalidStatusResultsInErrorState(
loginStatus: LoginStatus,
errorType: AuthViewModel.ErrorType
) {
@ -166,8 +169,9 @@ internal class AuthViewModelTest {
navigateToHomeObserver.assertNoValue()
}
@DisplayName("GIVEN answer success and login status success WHEN login called THEN navigation event is sent")
@Test
fun GIVEN_answer_success_login_status_success_WHEN_login_called_THEN_navigation_event_is_sent() {
fun successLoginResultsInNavigation() {
runBlocking {
whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Success(LoginStatus.SUCCESS))
}

View file

@ -24,22 +24,22 @@ class CodeKataAuthViewModel {
sut = AuthViewModel(mockLoginUseCase)
}
@DisplayName("GIVEN_initialized_viewModel_WHEN_observed_THEN_loading_false_other_fields_are_empty")
@DisplayName("GIVEN initialized viewModel WHEN observed THEN loading false other fields are empty")
@Test
fun initialSetup() {
}
@DisplayName("GIVEN_password_text_WHEN_onPasswordChanged_is_called_THEN_password_livedata_is_updated")
@DisplayName("GIVEN password text WHEN onPasswordChanged is called THEN password livedata is updated")
@Test
fun whenPasswordChangedLiveDataIsUpdated() {
}
@DisplayName("GIVEN_username_text_WHEN_onUsernameChanged_is_called_THEN_username_livedata_is_updated")
@DisplayName("GIVEN username text WHEN onUsernameChanged is called THEN username livedata is updated")
@Test
fun whenUsernameChangedLiveDataIsUpdated() {
}
@DisplayName("GIVEN_no_password_or_username_WHEN_login_is_Called_THEN_empty_credentials_are_used_in_usecase")
@DisplayName("GIVEN no password or username WHEN login is Called THEN empty credentials are used in usecase")
@Test
fun noPasswordUsesEmptyStringInLoginUseCase() {
}

View file

@ -16,6 +16,7 @@ import org.fnives.test.showcase.model.shared.Resource
import org.fnives.test.showcase.testutils.InstantExecutorExtension
import org.fnives.test.showcase.testutils.TestMainDispatcher
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
@ -55,16 +56,18 @@ internal class MainViewModelTest {
)
}
@DisplayName("WHEN initialization THEN error false other states empty")
@Test
fun WHEN_initialization_THEN_error_false_other_states_empty() {
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 GIVEN_initialized_viewModel_WHEN_loading_is_returned_THEN_loading_is_shown() {
fun loadingDataShowsInLoadingUIState() {
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading()))
testDispatcher.resumeDispatcher()
testDispatcher.advanceUntilIdle()
@ -75,8 +78,9 @@ internal class MainViewModelTest {
sut.navigateToAuth.test().assertNoValue()
}
@DisplayName("GIVEN loading then data WHEN observing content THEN proper states are shown")
@Test
fun GIVEN_loading_then_data_WHEN_observing_content_THEN_proper_states_are_shown() {
fun loadingThenLoadedDataResultsInProperUIStates() {
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading(), Resource.Success(emptyList())))
val errorMessageTestObserver = sut.errorMessage.test()
val contentTestObserver = sut.content.test()
@ -84,14 +88,15 @@ internal class MainViewModelTest {
testDispatcher.resumeDispatcher()
testDispatcher.advanceUntilIdle()
errorMessageTestObserver.assertValueHistory(false, false, false)
errorMessageTestObserver.assertValueHistory(false)
contentTestObserver.assertValueHistory(listOf())
loadingTestObserver.assertValueHistory(true, false)
sut.navigateToAuth.test().assertNoValue()
}
@DisplayName("GIVEN loading then error WHEN observing content THEN proper states are shown")
@Test
fun GIVEN_loading_then_error_WHEN_observing_content_THEN_proper_states_are_shown() {
fun loadingThenErrorResultsInProperUIStates() {
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading(), Resource.Error(Throwable())))
val errorMessageTestObserver = sut.errorMessage.test()
val contentTestObserver = sut.content.test()
@ -99,14 +104,15 @@ internal class MainViewModelTest {
testDispatcher.resumeDispatcher()
testDispatcher.advanceUntilIdle()
errorMessageTestObserver.assertValueHistory(false, false, true)
errorMessageTestObserver.assertValueHistory(false, true)
contentTestObserver.assertValueHistory(emptyList())
loadingTestObserver.assertValueHistory(true, false)
sut.navigateToAuth.test().assertNoValue()
}
@DisplayName("GIVEN loading then error then loading then data WHEN observing content THEN proper states are shown")
@Test
fun GIVEN_loading_then_error_then_loading_then_data_WHEN_observing_content_THEN_proper_states_are_shown() {
fun loadingThenErrorThenLoadingThenDataResultsInProperUIStates() {
val content = listOf(
FavouriteContent(Content(ContentId(""), "", "", ImageUrl("")), false)
)
@ -124,14 +130,15 @@ internal class MainViewModelTest {
testDispatcher.resumeDispatcher()
testDispatcher.advanceUntilIdle()
errorMessageTestObserver.assertValueHistory(false, false, true, false, false)
errorMessageTestObserver.assertValueHistory(false, true, false)
contentTestObserver.assertValueHistory(emptyList(), content)
loadingTestObserver.assertValueHistory(true, false, true, false)
sut.navigateToAuth.test().assertNoValue()
}
@DisplayName("GIVEN loading viewModel WHEN refreshing THEN usecase is not called")
@Test
fun GIVEN_loading_viewModel_WHEN_refreshing_THEN_usecase_is_not_called() {
fun fetchIsIgnoredIfViewModelIsStillLoading() {
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading()))
sut.content.test()
testDispatcher.resumeDispatcher()
@ -143,8 +150,9 @@ internal class MainViewModelTest {
verifyZeroInteractions(mockFetchContentUseCase)
}
@DisplayName("GIVEN non loading viewModel WHEN refreshing THEN usecase is called")
@Test
fun GIVEN_non_loading_viewModel_WHEN_refreshing_THEN_usecase_is_called() {
fun fetchIsCalledIfViewModelIsLoaded() {
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf())
sut.content.test()
testDispatcher.resumeDispatcher()
@ -157,8 +165,9 @@ internal class MainViewModelTest {
verifyNoMoreInteractions(mockFetchContentUseCase)
}
@DisplayName("GIVEN loading viewModel WHEN loging out THEN usecase is called")
@Test
fun GIVEN_loading_viewModel_WHEN_loging_out_THEN_usecase_is_called() {
fun loadingViewModelStillCalsLogout() {
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf(Resource.Loading()))
sut.content.test()
testDispatcher.resumeDispatcher()
@ -171,8 +180,9 @@ internal class MainViewModelTest {
verifyNoMoreInteractions(mockLogoutUseCase)
}
@DisplayName("GIVEN non loading viewModel WHEN loging out THEN usecase is called")
@Test
fun GIVEN_non_loading_viewModel_WHEN_loging_out_THEN_usecase_is_called() {
fun nonLoadingViewModelStillCalsLogout() {
whenever(mockGetAllContentUseCase.get()).doReturn(flowOf())
sut.content.test()
testDispatcher.resumeDispatcher()
@ -185,8 +195,9 @@ internal class MainViewModelTest {
verifyNoMoreInteractions(mockLogoutUseCase)
}
@DisplayName("GIVEN success content list viewModel WHEN toggling a nonexistent contentId THEN nothing happens")
@Test
fun GIVEN_success_content_list_viewModel_WHEN_toggling_a_nonexistent_contentId_THEN_nothing_happens() {
fun interactionWithNonExistentContentIdIsIgnored() {
val contents = listOf(
FavouriteContent(Content(ContentId("a"), "", "", ImageUrl("")), false),
FavouriteContent(Content(ContentId("b"), "", "", ImageUrl("")), true)
@ -203,8 +214,9 @@ internal class MainViewModelTest {
verifyZeroInteractions(mockAddContentToFavouriteUseCase)
}
@DisplayName("GIVEN success content list viewModel WHEN toggling a favourite contentId THEN remove favourite usecase is called")
@Test
fun GIVEN_success_content_list_viewModel_WHEN_toggling_a_favourite_contentId_THEN_remove_favourite_usecase_is_called() {
fun togglingFavouriteContentCallsRemoveFromFavourite() {
val contents = listOf(
FavouriteContent(Content(ContentId("a"), "", "", ImageUrl("")), false),
FavouriteContent(Content(ContentId("b"), "", "", ImageUrl("")), true)
@ -222,8 +234,9 @@ internal class MainViewModelTest {
verifyZeroInteractions(mockAddContentToFavouriteUseCase)
}
@DisplayName("GIVEN success content list viewModel WHEN toggling a not favourite contentId THEN add favourite usecase is called")
@Test
fun GIVEN_success_content_list_viewModel_WHEN_toggling_a_not_favourite_contentId_THEN_add_favourite_usecase_is_called() {
fun togglingNonFavouriteContentCallsAddToFavourite() {
val contents = listOf(
FavouriteContent(Content(ContentId("a"), "", "", ImageUrl("")), false),
FavouriteContent(Content(ContentId("b"), "", "", ImageUrl("")), true)

View file

@ -1,13 +1,15 @@
package org.fnives.test.showcase.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 GIVEN_event_WHEN_consumed_is_called_THEN_value_is_returned() {
fun consumedReturnsValue() {
val expected = "a"
val actual = Event("a").consume()
@ -15,8 +17,9 @@ internal class EventTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN consumed event WHEN consumed is called THEN null is returned")
@Test
fun GIVEN_consumed_event_WHEN_consumed_is_called_THEN_null_is_returned() {
fun consumedEventReturnsNull() {
val expected: String? = null
val event = Event("a")
event.consume()
@ -26,8 +29,9 @@ internal class EventTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN event WHEN peek is called THEN value is returned")
@Test
fun GIVEN_event_WHEN_peek_is_called_THEN_value_is_returned() {
fun peekReturnsValue() {
val expected = "a"
val actual = Event("a").peek()
@ -35,8 +39,9 @@ internal class EventTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN consumed event WHEN peek is called THEN value is returned")
@Test
fun GIVEN_consumed_event_WHEN_peek_is_called_THEN_value_is_returned() {
fun consumedEventPeekedReturnsValue() {
val expected = "a"
val event = Event("a")
event.consume()

View file

@ -10,7 +10,7 @@ buildscript {
}
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
classpath 'com.android.tools.build:gradle:7.0.3'
classpath 'com.android.tools.build:gradle:7.0.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jlleitschuh.gradle:ktlint-gradle:10.2.0"
}

View file

@ -6,6 +6,7 @@ import org.fnives.test.showcase.core.storage.content.FavouriteContentLocalStorag
import org.fnives.test.showcase.model.content.ContentId
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
@ -27,22 +28,27 @@ internal class AddContentToFavouriteUseCaseTest {
sut = AddContentToFavouriteUseCase(mockFavouriteContentLocalStorage)
}
@DisplayName("WHEN nothing happens THEN the storage is not touched")
@Test
fun WHEN_nothing_happens_THEN_the_storage_is_not_touched() {
fun initializationDoesntAffectStorage() {
verifyZeroInteractions(mockFavouriteContentLocalStorage)
}
@DisplayName("GIVEN contentId WHEN called THEN storage is called")
@Test
fun GIVEN_contentId_WHEN_called_THEN_storage_is_called() = runBlockingTest {
fun contentIdIsDelegatedToStorage() = runBlockingTest {
sut.invoke(ContentId("a"))
verify(mockFavouriteContentLocalStorage, times(1)).markAsFavourite(ContentId("a"))
verifyNoMoreInteractions(mockFavouriteContentLocalStorage)
}
@DisplayName("GIVEN throwing local storage WHEN thrown THEN its propagated")
@Test
fun GIVEN_throwing_local_storage_WHEN_thrown_THEN_its_thrown() = runBlockingTest {
whenever(mockFavouriteContentLocalStorage.markAsFavourite(ContentId("a"))).doThrow(RuntimeException())
fun storageThrowingIsPropagated() = runBlockingTest {
whenever(mockFavouriteContentLocalStorage.markAsFavourite(ContentId("a"))).doThrow(
RuntimeException()
)
assertThrows(RuntimeException::class.java) {
runBlocking { sut.invoke(ContentId("a")) }

View file

@ -4,6 +4,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runBlockingTest
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
@ -25,21 +26,24 @@ internal class FetchContentUseCaseTest {
sut = FetchContentUseCase(mockContentRepository)
}
@DisplayName("WHEN nothing happens THEN the storage is not touched")
@Test
fun WHEN_nothing_happens_THEN_the_storage_is_not_touched() {
fun initializationDoesntAffectRepository() {
verifyZeroInteractions(mockContentRepository)
}
@DisplayName("WHEN called THEN repository is called")
@Test
fun WHEN_called_THEN_repository_is_called() = runBlockingTest {
fun whenCalledRepositoryIsFetched() = runBlockingTest {
sut.invoke()
verify(mockContentRepository, times(1)).fetch()
verifyNoMoreInteractions(mockContentRepository)
}
@DisplayName("GIVEN throwing local storage WHEN thrown THEN its thrown")
@Test
fun GIVEN_throwing_local_storage_WHEN_thrown_THEN_its_thrown() = runBlockingTest {
fun whenRepositoryThrowsUseCaseAlsoThrows() = runBlockingTest {
whenever(mockContentRepository.fetch()).doThrow(RuntimeException())
assertThrows(RuntimeException::class.java) {

View file

@ -15,6 +15,7 @@ import org.fnives.test.showcase.model.content.ImageUrl
import org.fnives.test.showcase.model.shared.Resource
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
@ -45,8 +46,9 @@ internal class GetAllContentUseCaseTest {
sut = GetAllContentUseCase(mockContentRepository, mockFavouriteContentLocalStorage)
}
@DisplayName("GIVEN loading AND empty favourite WHEN observed THEN loading is shown")
@Test
fun GIVEN_loading_AND_empty_favourite_WHEN_observed_THEN_loading_is_shown() =
fun loadingResourceWithNoFavouritesResultsInLoadingResource() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = emptyList()
contentFlow.value = Resource.Loading()
@ -57,8 +59,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(listOf(expected), actual)
}
@DisplayName("GIVEN loading AND listOfFavourite WHEN observed THEN loading is shown")
@Test
fun GIVEN_loading_AND_listOfFavourite_WHEN_observed_THEN_loading_is_shown() =
fun loadingResourceWithFavouritesResultsInLoadingResource() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = listOf(ContentId("a"))
contentFlow.value = Resource.Loading()
@ -69,8 +72,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(listOf(expected), actual)
}
@DisplayName("GIVEN error AND empty favourite WHEN observed THEN error is shown")
@Test
fun GIVEN_error_AND_empty_favourite_WHEN_observed_THEN_error_is_shown() =
fun errorResourceWithNoFavouritesResultsInErrorResource() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = emptyList()
val exception = Throwable()
@ -82,8 +86,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(listOf(expected), actual)
}
@DisplayName("GIVEN error AND listOfFavourite WHEN observed THEN error is shown")
@Test
fun GIVEN_error_AND_listOfFavourite_WHEN_observed_THEN_error_is_shown() =
fun errorResourceWithFavouritesResultsInErrorResource() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = listOf(ContentId("b"))
val exception = Throwable()
@ -95,8 +100,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(listOf(expected), actual)
}
@DisplayName("GIVEN listOfContent AND empty favourite WHEN observed THEN favourites are returned")
@Test
fun GIVEN_listOfContent_AND_empty_favourite_WHEN_observed_THEN_favourites_are_returned() =
fun successResourceWithNoFavouritesResultsInNoFavouritedItems() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = emptyList()
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))
@ -111,8 +117,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(listOf(expected), actual)
}
@DisplayName("GIVEN listOfContent AND other favourite id WHEN observed THEN favourites are returned")
@Test
fun GIVEN_listOfContent_AND_other_favourite_id_WHEN_observed_THEN_favourites_are_returned() =
fun successResourceWithDifferentFavouritesResultsInNoFavouritedItems() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = listOf(ContentId("x"))
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))
@ -127,8 +134,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(listOf(expected), actual)
}
@DisplayName("GIVEN listOfContent AND same favourite id WHEN observed THEN favourites are returned")
@Test
fun GIVEN_listOfContent_AND_same_favourite_id_WHEN_observed_THEN_favourites_are_returned() =
fun successResourceWithSameFavouritesResultsInFavouritedItems() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = listOf(ContentId("a"))
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))
@ -143,8 +151,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(listOf(expected), actual)
}
@DisplayName("GIVEN loading then data then added favourite WHEN observed THEN loading then correct favourites are returned")
@Test
fun GIVEN_loading_then_data_then_added_favourite_WHEN_observed_THEN_loading_then_correct_favourites_are_returned() =
fun whileLoadingAndAddingItemsReactsProperly() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = emptyList()
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))
@ -169,8 +178,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(expected, actual.await())
}
@DisplayName("GIVEN loading then data then removed favourite WHEN observed THEN loading then correct favourites are returned")
@Test
fun GIVEN_loading_then_data_then_removed_favourite_WHEN_observed_THEN_loading_then_correct_favourites_are_returned() =
fun whileLoadingAndRemovingItemsReactsProperly() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = listOf(ContentId("a"))
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))
@ -195,8 +205,9 @@ internal class GetAllContentUseCaseTest {
Assertions.assertEquals(expected, actual.await())
}
@DisplayName("GIVEN loading then data then loading WHEN observed THEN loading then correct favourites then loading are returned")
@Test
fun GIVEN_loading_then_data_then_loading_WHEN_observed_THEN_loading_then_correct_favourites_then_loadingare_returned() =
fun loadingThenDataThenLoadingReactsProperly() =
runBlockingTest(testDispatcher) {
favouriteContentIdFlow.value = listOf(ContentId("a"))
val content = Content(ContentId("a"), "b", "c", ImageUrl("d"))

View file

@ -6,6 +6,7 @@ import org.fnives.test.showcase.core.storage.content.FavouriteContentLocalStorag
import org.fnives.test.showcase.model.content.ContentId
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doThrow
import org.mockito.kotlin.mock
@ -27,21 +28,24 @@ internal class RemoveContentFromFavouritesUseCaseTest {
sut = RemoveContentFromFavouritesUseCase(mockFavouriteContentLocalStorage)
}
@DisplayName("WHEN nothing happens THEN the storage is not touched")
@Test
fun WHEN_nothing_happens_THEN_the_storage_is_not_touched() {
fun initializationDoesntAffectStorage() {
verifyZeroInteractions(mockFavouriteContentLocalStorage)
}
@DisplayName("GIVEN contentId WHEN called THEN storage is called")
@Test
fun GIVEN_contentId_WHEN_called_THEN_storage_is_called() = runBlockingTest {
fun givenContentIdCallsStorage() = runBlockingTest {
sut.invoke(ContentId("a"))
verify(mockFavouriteContentLocalStorage, times(1)).deleteAsFavourite(ContentId("a"))
verifyNoMoreInteractions(mockFavouriteContentLocalStorage)
}
@DisplayName("GIVEN throwing local storage WHEN thrown THEN its propogated")
@Test
fun GIVEN_throwing_local_storage_WHEN_thrown_THEN_its_thrown() = runBlockingTest {
fun storageExceptionThrowingIsPropogated() = runBlockingTest {
whenever(mockFavouriteContentLocalStorage.deleteAsFavourite(ContentId("a"))).doThrow(RuntimeException())
Assertions.assertThrows(RuntimeException::class.java) {

View file

@ -4,6 +4,7 @@ import org.fnives.test.showcase.core.storage.UserDataLocalStorage
import org.fnives.test.showcase.model.session.Session
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
@ -22,13 +23,15 @@ internal class IsUserLoggedInUseCaseTest {
sut = IsUserLoggedInUseCase(mockUserDataLocalStorage)
}
@DisplayName("WHEN nothing is called THEN storage is not called")
@Test
fun WHEN_nothing_is_called_THEN_storage_is_not_called() {
fun creatingDoesntAffectStorage() {
verifyZeroInteractions(mockUserDataLocalStorage)
}
@DisplayName("GIVEN session data saved WHEN is user logged in checked THEN true is returned")
@Test
fun GIVEN_session_data_saved_WHEN_is_user_logged_in_checked_THEN_true_is_returned() {
fun sessionInStorageResultsInLoggedIn() {
whenever(mockUserDataLocalStorage.session).doReturn(Session("a", "b"))
val actual = sut.invoke()
@ -36,8 +39,9 @@ internal class IsUserLoggedInUseCaseTest {
Assertions.assertEquals(true, actual)
}
@DisplayName("GIVEN no session data saved WHEN is user logged in checked THEN false is returned")
@Test
fun GIVEN_no_session_data_saved_WHEN_is_user_logged_in_checked_THEN_false_is_returned() {
fun noSessionInStorageResultsInLoggedOut() {
whenever(mockUserDataLocalStorage.session).doReturn(null)
val actual = sut.invoke()
@ -45,8 +49,9 @@ internal class IsUserLoggedInUseCaseTest {
Assertions.assertEquals(false, actual)
}
@DisplayName("GIVEN no session THEN session THEN no session WHEN is user logged in checked over again THEN every return is correct")
@Test
fun GIVEN_no_session_THEN_session_THEN_no_session_WHEN_is_user_logged_in_checked_over_again_THEN_every_return_is_correct() {
fun multipleSessionSettingsResultsInCorrectResponses() {
whenever(mockUserDataLocalStorage.session).doReturn(null)
val actual1 = sut.invoke()
whenever(mockUserDataLocalStorage.session).doReturn(Session("", ""))

View file

@ -6,6 +6,7 @@ import org.fnives.test.showcase.core.login.LogoutUseCase
import org.fnives.test.showcase.core.storage.UserDataLocalStorage
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.times
@ -36,13 +37,15 @@ internal class LogoutUseCaseTest {
testCoreComponent.inject(this)
}
@DisplayName("WHEN no call THEN storage is not interacted")
@Test
fun WHEN_no_call_THEN_storage_is_not_interacted() {
fun initializedDoesntAffectStorage() {
verifyZeroInteractions(mockUserDataLocalStorage)
}
@DisplayName("WHEN logout invoked THEN storage is cleared")
@Test
fun WHEN_logout_invoked_THEN_storage_is_cleared() = runBlockingTest {
fun logoutResultsInStorageCleaning() = runBlockingTest {
val repositoryBefore = contentRepository
sut.invoke()

View file

@ -9,6 +9,7 @@ import org.fnives.test.showcase.model.network.BaseUrl
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
@ -47,13 +48,15 @@ internal class LogoutUseCaseTest : KoinTest {
stopKoin()
}
@DisplayName("WHEN no call THEN storage is not interacted")
@Test
fun WHEN_no_call_THEN_storage_is_not_interacted() {
fun initializedDoesntAffectStorage() {
verifyZeroInteractions(mockUserDataLocalStorage)
}
@DisplayName("WHEN logout invoked THEN storage is cleared")
@Test
fun WHEN_logout_invoked_THEN_storage_is_cleared() = runBlockingTest {
fun logoutResultsInStorageCleaning() = runBlockingTest {
val repositoryBefore = getKoin().get<ContentRepository>()
sut.invoke()

View file

@ -7,13 +7,15 @@ import org.fnives.test.showcase.model.shared.Resource
import org.fnives.test.showcase.network.shared.exceptions.NetworkException
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
@Suppress("TestFunctionName")
internal class AnswerUtilsKtTest {
@DisplayName("GIVEN network exception thrown WHEN wrapped into answer THEN answer error is returned")
@Test
fun GIVEN_network_exception_thrown_WHEN_wrapped_into_answer_THEN_answer_error_is_returned() = runBlocking {
fun networkExceptionThrownResultsInError() = runBlocking {
val exception = NetworkException(Throwable())
val expected = Answer.Error<Unit>(exception)
@ -22,8 +24,9 @@ internal class AnswerUtilsKtTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN parsing exception thrown WHEN wrapped into answer THEN answer error is returned")
@Test
fun GIVEN_parsing_exception_thrown_WHEN_wrapped_into_answer_THEN_answer_error_is_returned() = runBlocking {
fun parsingExceptionThrownResultsInError() = runBlocking {
val exception = ParsingException(Throwable())
val expected = Answer.Error<Unit>(exception)
@ -32,8 +35,9 @@ internal class AnswerUtilsKtTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN unexpected throwable thrown WHEN wrapped into answer THEN answer error is returned")
@Test
fun GIVEN_parsing_throwable_thrown_WHEN_wrapped_into_answer_THEN_answer_error_is_returned() = runBlocking {
fun unexpectedExceptionThrownResultsInError() = runBlocking {
val exception = Throwable()
val expected = Answer.Error<Unit>(UnexpectedException(exception))
@ -42,8 +46,9 @@ internal class AnswerUtilsKtTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN string WHEN wrapped into answer THEN string answer is returned")
@Test
fun GIVEN_string_WHEN_wrapped_into_answer_THEN_string_answer_is_returned() = runBlocking {
fun stringIsReturnedWrappedIntoSuccess() = runBlocking {
val expected = Answer.Success("banan")
val actual = wrapIntoAnswer { "banan" }
@ -51,15 +56,17 @@ internal class AnswerUtilsKtTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN cancellation exception WHEN wrapped into answer THEN cancellation exception is thrown")
@Test
fun GIVEN_cancellation_exception_WHEN_wrapped_into_answer_THEN_cancellation_exception_is_thrown() {
fun cancellationExceptionResultsInThrowingIt() {
Assertions.assertThrows(CancellationException::class.java) {
runBlocking { wrapIntoAnswer { throw CancellationException() } }
}
}
@DisplayName("GIVEN success answer WHEN converted into resource THEN Resource success is returned")
@Test
fun GIVEN_success_answer_WHEN_converted_into_resource_THEN_Resource_success_is_returned() {
fun successAnswerConvertsToSuccessResource() {
val expected = Resource.Success("alma")
val actual = Answer.Success("alma").mapIntoResource()
@ -67,8 +74,9 @@ internal class AnswerUtilsKtTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN error answer WHEN converted into resource THEN Resource error is returned")
@Test
fun GIVEN_error_answer_WHEN_converted_into_resource_THEN_Resource_error_is_returned() {
fun errorAnswerConvertsToErrorResource() {
val exception = Throwable()
val expected = Resource.Error<Unit>(exception)

View file

@ -3,6 +3,7 @@ package org.fnives.test.showcase.core.storage
import org.fnives.test.showcase.model.session.Session
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
@ -23,16 +24,18 @@ internal class NetworkSessionLocalStorageAdapterTest {
sut = NetworkSessionLocalStorageAdapter(mockUserDataLocalStorage)
}
@DisplayName("GIVEN null as session WHEN saved THEN its delegated")
@Test
fun GIVEN_null_as_session_WHEN_saved_THEN_its_delegated() {
fun settingNullSessionIsDelegated() {
sut.session = null
verify(mockUserDataLocalStorage, times(1)).session = null
verifyNoMoreInteractions(mockUserDataLocalStorage)
}
@DisplayName("GIVEN session WHEN saved THEN its delegated")
@Test
fun GIVEN_session_WHEN_saved_THEN_its_delegated() {
fun settingDataAsSessionIsDelegated() {
val expected = Session("a", "b")
sut.session = Session("a", "b")
@ -41,8 +44,9 @@ internal class NetworkSessionLocalStorageAdapterTest {
verifyNoMoreInteractions(mockUserDataLocalStorage)
}
@DisplayName("WHEN session requested THEN its returned from delegated")
@Test
fun WHEN_session_requested_THEN_its_returned_from_delegated() {
fun gettingSessionReturnsFromDelegate() {
val expected = Session("a", "b")
whenever(mockUserDataLocalStorage.session).doReturn(expected)

View file

@ -16,7 +16,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
android.enableJetifier=false
android.jetifier.ignorelist=bcprov-jdk15on
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

View file

@ -1,14 +1,14 @@
project.ext {
androidx_core_version = "1.7.0"
androidx_appcompat_version = "1.3.1"
androidx_appcompat_version = "1.4.0"
androidx_material_version = "1.4.0"
androidx_constraintlayout_version = "2.1.1"
androidx_constraintlayout_version = "2.1.2"
androidx_livedata_version = "2.4.0"
androidx_swiperefreshlayout_version = "1.1.0"
androidx_room_version = "2.3.0"
androidx_room_version = "2.4.0"
activity_ktx_version = "1.4.0"
coroutines_version = "1.4.3"
coroutines_version = "1.5.2"
koin_version = "3.1.2"
coil_version = "1.1.1"
retrofit_version = "2.9.0"
@ -26,5 +26,4 @@ project.ext {
testing_junit4_version = "4.13.2"
testing_robolectric_version = "4.6.1"
testing_espresso_version = "3.4.0"
testing_okhttp3_idling_resource_version = "1.0.0"
}

View file

@ -35,7 +35,7 @@ class CodeKataLoginRemoteSourceTest {
fun badRequestMeansInvalidCredentials() = runBlocking {
}
@DisplayName("GIVEN_internal_error_response_WHEN_request_is_fired_THEN_network_exception_is_thrown")
@DisplayName("GIVEN internal error response WHEN request is fired THEN network exception is thrown")
@Test
fun genericErrorMeansNetworkError() {
}

View file

@ -12,6 +12,7 @@ import org.fnives.test.showcase.network.shared.exceptions.NetworkException
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import retrofit2.Response
import java.io.IOException
@ -26,8 +27,9 @@ class LoginErrorConverterTest {
sut = LoginErrorConverter()
}
@DisplayName("GIVEN throwing lambda WHEN parsing login error THEN network exception is thrown")
@Test
fun GIVEN_throwing_lambda_WHEN_parsing_login_error_THEN_network_exception_is_thrown() {
fun generallyThrowingLambdaResultsInNetworkException() {
Assertions.assertThrows(NetworkException::class.java) {
runBlocking {
sut.invoke { throw IOException() }
@ -35,8 +37,9 @@ class LoginErrorConverterTest {
}
}
@DisplayName("GIVEN jsonException throwing lambda WHEN parsing login error THEN network exception is thrown")
@Test
fun GIVEN_jsonException_throwing_lambda_WHEN_parsing_login_error_THEN_network_exception_is_thrown() {
fun jsonDataThrowingLambdaResultsInParsingException() {
Assertions.assertThrows(ParsingException::class.java) {
runBlocking {
sut.invoke { throw JsonDataException("") }
@ -44,8 +47,9 @@ class LoginErrorConverterTest {
}
}
@DisplayName("GIVEN 400 error response WHEN parsing login error THEN invalid credentials is returned")
@Test
fun GIVEN_400_error_response_WHEN_parsing_login_error_THEN_invalid_credentials_is_returned() = runBlockingTest {
fun code400ResponseResultsInInvalidCredentials() = runBlockingTest {
val expected = LoginStatusResponses.InvalidCredentials
val actual = sut.invoke {
@ -56,8 +60,9 @@ class LoginErrorConverterTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN successful response WHEN parsing login error THEN successful response is returned")
@Test
fun GIVEN_successful_response_WHEN_parsing_login_error_THEN_successful_response_is_returned() = runBlockingTest {
fun successResponseResultsInSessionResponse() = runBlockingTest {
val loginResponse = LoginResponse("a", "r")
val expectedSession = Session(accessToken = loginResponse.accessToken, refreshToken = loginResponse.refreshToken)
val expected = LoginStatusResponses.Success(expectedSession)

View file

@ -11,6 +11,7 @@ import org.fnives.test.showcase.network.shared.exceptions.NetworkException
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import org.mockito.kotlin.mock
@ -41,8 +42,9 @@ class LoginRemoteSourceRefreshActionImplTest {
.inject(this)
}
@DisplayName("GIVEN successful response WHEN refresh request is fired THEN session is returned")
@Test
fun GIVEN_successful_response_WHEN_refresh_request_is_fired_THEN_session() = runBlocking {
fun successResponseResultsInSession() = runBlocking {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success)
val expected = ContentData.refreshSuccessResponse
@ -51,9 +53,9 @@ class LoginRemoteSourceRefreshActionImplTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN successful response WHEN refresh request is fired THEN the request is setup properly")
@Test
fun GIVEN_successful_response_WHEN_refresh_request_is_fired_THEN_the_request_is_setup_properly() =
runBlocking {
fun refreshRequestIsSetupProperly() = runBlocking {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success, false)
sut.refresh(ContentData.refreshSuccessResponse.refreshToken)
@ -69,8 +71,9 @@ class LoginRemoteSourceRefreshActionImplTest {
Assertions.assertEquals("", request.body.readUtf8())
}
@DisplayName("GIVEN internal error response WHEN refresh request is fired THEN network exception is thrown")
@Test
fun GIVEN_internal_error_response_WHEN_refresh_request_is_fired_THEN_network_exception_is_thrown() {
fun generalErrorResponseResultsInNetworkException() {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Error)
Assertions.assertThrows(NetworkException::class.java) {
@ -78,8 +81,9 @@ class LoginRemoteSourceRefreshActionImplTest {
}
}
@DisplayName("GIVEN invalid json response WHEN refresh request is fired THEN network exception is thrown")
@Test
fun GIVEN_invalid_json_response_WHEN_refresh_request_is_fired_THEN_network_exception_is_thrown() {
fun jsonErrorResponseResultsInParsingException() {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.UnexpectedJsonAsSuccessResponse)
Assertions.assertThrows(ParsingException::class.java) {
@ -87,8 +91,9 @@ class LoginRemoteSourceRefreshActionImplTest {
}
}
@DisplayName("GIVEN malformed json response WHEN refresh request is fired THEN parsing exception is thrown")
@Test
fun GIVEN_malformed_json_response_WHEN_refresh_request_is_fired_THEN_network_exception_is_thrown() {
fun malformedJsonErrorResponseResultsInParsingException() {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.MalformedJson)
Assertions.assertThrows(ParsingException::class.java) {

View file

@ -88,7 +88,7 @@ class LoginRemoteSourceTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN_internal_error_response_WHEN_request_is_fired_THEN_network_exception_is_thrown")
@DisplayName("GIVEN internal error response WHEN request is fired THEN network exception is thrown")
@Test
fun genericErrorMeansNetworkError() {
mockServerScenarioSetup.setScenario(AuthScenario.GenericError("a", "b"))

View file

@ -13,6 +13,7 @@ import org.fnives.test.showcase.network.shared.exceptions.ParsingException
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import org.koin.core.context.startKoin
@ -53,8 +54,9 @@ class LoginRemoteSourceRefreshActionImplTest : KoinTest {
stopKoin()
}
@DisplayName("GIVEN successful response WHEN refresh request is fired THEN session is returned")
@Test
fun GIVEN_successful_response_WHEN_refresh_request_is_fired_THEN_session() = runBlocking {
fun successResponseResultsInSession() = runBlocking {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success)
val expected = ContentData.refreshSuccessResponse
@ -63,8 +65,9 @@ class LoginRemoteSourceRefreshActionImplTest : KoinTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN successful response WHEN refresh request is fired THEN the request is setup properly")
@Test
fun GIVEN_successful_response_WHEN_refresh_request_is_fired_THEN_the_request_is_setup_properly() = runBlocking {
fun refreshRequestIsSetupProperly() = runBlocking {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Success, false)
sut.refresh(ContentData.refreshSuccessResponse.refreshToken)
@ -77,8 +80,9 @@ class LoginRemoteSourceRefreshActionImplTest : KoinTest {
Assertions.assertEquals("", request.body.readUtf8())
}
@DisplayName("GIVEN internal error response WHEN refresh request is fired THEN network exception is thrown")
@Test
fun GIVEN_internal_error_response_WHEN_refresh_request_is_fired_THEN_network_exception_is_thrown() {
fun generalErrorResponseResultsInNetworkException() {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.Error)
Assertions.assertThrows(NetworkException::class.java) {
@ -86,8 +90,9 @@ class LoginRemoteSourceRefreshActionImplTest : KoinTest {
}
}
@DisplayName("GIVEN invalid json response WHEN refresh request is fired THEN network exception is thrown")
@Test
fun GIVEN_invalid_json_response_WHEN_refresh_request_is_fired_THEN_network_exception_is_thrown() {
fun jsonErrorResponseResultsInParsingException() {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.UnexpectedJsonAsSuccessResponse)
Assertions.assertThrows(ParsingException::class.java) {
@ -95,8 +100,9 @@ class LoginRemoteSourceRefreshActionImplTest : KoinTest {
}
}
@DisplayName("GIVEN malformed json response WHEN refresh request is fired THEN parsing exception is thrown")
@Test
fun GIVEN_malformed_json_response_WHEN_refresh_request_is_fired_THEN_network_exception_is_thrown() {
fun malformedJsonErrorResponseResultsInParsingException() {
mockServerScenarioSetup.setScenario(RefreshTokenScenario.MalformedJson)
Assertions.assertThrows(ParsingException::class.java) {

View file

@ -100,7 +100,7 @@ class LoginRemoteSourceTest : KoinTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN_internal_error_response_WHEN_request_is_fired_THEN_network_exception_is_thrown")
@DisplayName("GIVEN internal error response WHEN request is fired THEN network exception is thrown")
@Test
fun genericErrorMeansNetworkError() {
mockServerScenarioSetup.setScenario(AuthScenario.GenericError("a", "b"))

View file

@ -49,7 +49,7 @@ class CodeKataSessionExpirationTest : KoinTest {
mockWebServer.shutdown()
}
@DisplayName("GIVEN_401_THEN_refresh_token_ok_response_WHEN_content_requested_THE_tokens_are_refreshed_and_request_retried_with_new_tokens")
@DisplayName("GIVEN 401 THEN refresh token ok response WHEN content requested THE tokens are refreshed and request retried with new tokens")
@Test
fun successRefreshResultsInRequestRetry() = runBlocking {
}

View file

@ -11,6 +11,7 @@ import org.fnives.test.showcase.network.shared.exceptions.NetworkException
import org.fnives.test.showcase.network.shared.exceptions.ParsingException
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import org.koin.test.inject
@ -44,8 +45,9 @@ class ContentRemoteSourceImplTest {
.inject(this)
}
@DisplayName("GIVEN successful response WHEN getting content THEN its parsed and returned correctly")
@Test
fun GIVEN_successful_response_WHEN_getting_content_THEN_its_parsed_and_returned_correctly() = runBlocking {
fun successResponseParsing() = runBlocking {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.Success(false))
val expected = ContentData.contentSuccess
@ -55,8 +57,9 @@ class ContentRemoteSourceImplTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN successful response WHEN getting content THEN the request is setup properly")
@Test
fun GIVEN_successful_response_WHEN_getting_content_THEN_the_request_is_setup_properly() = runBlocking {
fun successResponseRequestIsCorrect() = runBlocking {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.Success(false), false)
@ -70,8 +73,9 @@ class ContentRemoteSourceImplTest {
Assertions.assertEquals("", request.body.readUtf8())
}
@DisplayName("GIVEN response with missing Field WHEN getting content THEN invalid is ignored others are returned")
@Test
fun GIVEN_response_with_missing_Field_WHEN_getting_content_THEN_invalid_is_ignored_others_are_returned() = runBlocking {
fun dataMissingFieldIsIgnored() = runBlocking {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.SuccessWithMissingFields(false))
@ -82,8 +86,9 @@ class ContentRemoteSourceImplTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN error response WHEN getting content THEN network request is thrown")
@Test
fun GIVEN_error_response_WHEN_getting_content_THEN_network_request_is_thrown() {
fun errorResponseResultsInNetworkException() {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.Error(false))
@ -92,8 +97,9 @@ class ContentRemoteSourceImplTest {
}
}
@DisplayName("GIVEN unexpected json response WHEN getting content THEN parsing request is thrown")
@Test
fun GIVEN_unexpected_json_response_WHEN_getting_content_THEN_parsing_request_is_thrown() {
fun unexpectedJSONResultsInParsingException() {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.UnexpectedJsonAsSuccessResponse(false))
@ -102,8 +108,9 @@ class ContentRemoteSourceImplTest {
}
}
@DisplayName("GIVEN malformed json response WHEN getting content THEN parsing request is thrown")
@Test
fun GIVEN_malformed_json_response_WHEN_getting_content_THEN_parsing_request_is_thrown() {
fun malformedJSONResultsInParsingException() {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.MalformedJsonAsSuccessResponse(false))

View file

@ -55,7 +55,7 @@ class SessionExpirationTest {
.inject(this)
}
@DisplayName("GIVEN_401_THEN_refresh_token_ok_response_WHEN_content_requested_THE_tokens_are_refreshed_and_request_retried_with_new_tokens")
@DisplayName("GIVEN 401 THEN refresh token ok response WHEN content requested THE tokens are refreshed and request retried with new tokens")
@Test
fun successRefreshResultsInRequestRetry() = runBlocking {
var sessionToReturnByMock: Session? = ContentData.loginSuccessResponse

View file

@ -13,6 +13,7 @@ import org.fnives.test.showcase.network.shared.exceptions.ParsingException
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.RegisterExtension
import org.koin.core.context.startKoin
@ -55,8 +56,9 @@ class ContentRemoteSourceImplTest : KoinTest {
stopKoin()
}
@DisplayName("GIVEN successful response WHEN getting content THEN its parsed and returned correctly")
@Test
fun GIVEN_successful_response_WHEN_getting_content_THEN_its_parsed_and_returned_correctly() = runBlocking {
fun successResponseParsing() = runBlocking {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.Success(false))
val expected = ContentData.contentSuccess
@ -66,8 +68,9 @@ class ContentRemoteSourceImplTest : KoinTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN successful response WHEN getting content THEN the request is setup properly")
@Test
fun GIVEN_successful_response_WHEN_getting_content_THEN_the_request_is_setup_properly() = runBlocking {
fun successResponseRequestIsCorrect() = runBlocking {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.Success(false), false)
@ -81,8 +84,9 @@ class ContentRemoteSourceImplTest : KoinTest {
Assertions.assertEquals("", request.body.readUtf8())
}
@DisplayName("GIVEN response with missing Field WHEN getting content THEN invalid is ignored others are returned")
@Test
fun GIVEN_response_with_missing_Field_WHEN_getting_content_THEN_invalid_is_ignored_others_are_returned() = runBlocking {
fun dataMissingFieldIsIgnored() = runBlocking {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.SuccessWithMissingFields(false))
@ -93,8 +97,9 @@ class ContentRemoteSourceImplTest : KoinTest {
Assertions.assertEquals(expected, actual)
}
@DisplayName("GIVEN error response WHEN getting content THEN network request is thrown")
@Test
fun GIVEN_error_response_WHEN_getting_content_THEN_network_request_is_thrown() {
fun errorResponseResultsInNetworkException() {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.Error(false))
@ -103,8 +108,9 @@ class ContentRemoteSourceImplTest : KoinTest {
}
}
@DisplayName("GIVEN unexpected json response WHEN getting content THEN parsing request is thrown")
@Test
fun GIVEN_unexpected_json_response_WHEN_getting_content_THEN_parsing_request_is_thrown() {
fun unexpectedJSONResultsInParsingException() {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.UnexpectedJsonAsSuccessResponse(false))
@ -113,8 +119,9 @@ class ContentRemoteSourceImplTest : KoinTest {
}
}
@DisplayName("GIVEN malformed json response WHEN getting content THEN parsing request is thrown")
@Test
fun GIVEN_malformed_json_response_WHEN_getting_content_THEN_parsing_request_is_thrown() {
fun malformedJSONResultsInParsingException() {
whenever(mockNetworkSessionLocalStorage.session).doReturn(ContentData.loginSuccessResponse)
mockServerScenarioSetup.setScenario(ContentScenario.MalformedJsonAsSuccessResponse(false))

View file

@ -66,7 +66,7 @@ class SessionExpirationTest : KoinTest {
stopKoin()
}
@DisplayName("GIVEN_401_THEN_refresh_token_ok_response_WHEN_content_requested_THE_tokens_are_refreshed_and_request_retried_with_new_tokens")
@DisplayName("GIVEN 401 THEN refresh token ok response WHEN content requested THE tokens are refreshed and request retried with new tokens")
@Test
fun successRefreshResultsInRequestRetry() = runBlocking {
var sessionToReturnByMock: Session? = ContentData.loginSuccessResponse