Browse Source

Add some ProfileFragmentViewModel logic and tests

renovate/org.robolectric-robolectric-4.x
Ammar Githam 4 years ago
parent
commit
70ffac3025
  1. 3
      app/build.gradle
  2. 2
      app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java
  3. 46
      app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt
  4. 41
      app/src/test/java/awais/instagrabber/LiveDataTestUtil.kt
  5. 47
      app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt

3
app/build.gradle

@ -242,9 +242,10 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'
testImplementation "androidx.test.ext:junit-ktx:1.1.2" testImplementation "androidx.test.ext:junit-ktx:1.1.2"
testImplementation "androidx.test:core-ktx:1.3.0" testImplementation "androidx.test:core-ktx:1.3.0"
testImplementation "androidx.arch.core:core-testing:2.1.0"
testImplementation "org.robolectric:robolectric:4.5.1" testImplementation "org.robolectric:robolectric:4.5.1"
androidTestImplementation 'org.junit.jupiter:junit-jupiter:5.7.1'
androidTestImplementation 'org.junit.jupiter:junit-jupiter:5.7.2'
androidTestImplementation 'androidx.test:core:1.3.0' androidTestImplementation 'androidx.test:core:1.3.0'
androidTestImplementation 'com.android.support:support-annotations:28.0.0' androidTestImplementation 'com.android.support:support-annotations:28.0.0'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'

2
app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.java

@ -382,7 +382,7 @@ public class ProfileFragment extends Fragment implements SwipeRefreshLayout.OnRe
shouldRefresh = false; shouldRefresh = false;
return root; return root;
} }
// appStateViewModel.getCurrentUserLiveData().observe(getViewLifecycleOwner(), user -> viewModel.setCurrentUser(user));
appStateViewModel.getCurrentUserLiveData().observe(getViewLifecycleOwner(), user -> viewModel.setCurrentUser(user));
binding = FragmentProfileBinding.inflate(inflater, container, false); binding = FragmentProfileBinding.inflate(inflater, container, false);
root = binding.getRoot(); root = binding.getRoot();
profileDetailsBinding = binding.header; profileDetailsBinding = binding.header;

46
app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt

@ -1,13 +1,12 @@
package awais.instagrabber.viewmodels package awais.instagrabber.viewmodels
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.lifecycle.* import androidx.lifecycle.*
import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.SavedStateRegistryOwner
import awais.instagrabber.db.repositories.AccountRepository import awais.instagrabber.db.repositories.AccountRepository
import awais.instagrabber.db.repositories.FavoriteRepository import awais.instagrabber.db.repositories.FavoriteRepository
import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.extensions.TAG
import awais.instagrabber.webservices.* import awais.instagrabber.webservices.*
class ProfileFragmentViewModel( class ProfileFragmentViewModel(
@ -20,17 +19,44 @@ class ProfileFragmentViewModel(
accountRepository: AccountRepository, accountRepository: AccountRepository,
favoriteRepository: FavoriteRepository, favoriteRepository: FavoriteRepository,
) : ViewModel() { ) : ViewModel() {
private val _profile = MutableLiveData<User?>()
val profile: LiveData<User?> = _profile
val username: LiveData<String> = Transformations.map(profile) { return@map it?.username ?: "" }
private val _profile = MutableLiveData<Resource<User?>>(Resource.loading(null))
private val _isLoggedIn = MutableLiveData(false)
var currentUser: User? = null
var isLoggedIn = false
get() = currentUser != null
private set
val profile: LiveData<Resource<User?>> = _profile
/**
* Username of profile without '`@`'
*/
val username: LiveData<String> = Transformations.map(profile) {
return@map when (it.status) {
Resource.Status.LOADING, Resource.Status.ERROR -> ""
Resource.Status.SUCCESS -> it.data?.username ?: ""
}
}
val isLoggedIn: LiveData<Boolean> = _isLoggedIn
var currentUser: Resource<User?>? = null
set(value) {
_isLoggedIn.postValue(value?.data != null)
// if no profile, and value is valid, set it as profile
val profileValue = profile.value
if (
profileValue?.status != Resource.Status.LOADING
&& profileValue?.data == null
&& value?.status == Resource.Status.SUCCESS
&& value.data != null
) {
_profile.postValue(Resource.success(value.data))
}
field = value
}
init { init {
Log.d(TAG, "${state.keys()} $userRepository $friendshipRepository $storiesRepository $mediaRepository")
// Log.d(TAG, "${state.keys()} $userRepository $friendshipRepository $storiesRepository $mediaRepository")
val usernameFromState = state.get<String?>("username")
if (usernameFromState.isNullOrBlank()) {
_profile.postValue(Resource.success(null))
}
} }
} }

41
app/src/test/java/awais/instagrabber/LiveDataTestUtil.kt

@ -0,0 +1,41 @@
package awais.instagrabber
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
@Suppress("UNCHECKED_CAST")
return data as T
}

47
app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt

@ -1,5 +1,6 @@
package awais.instagrabber.viewmodels package awais.instagrabber.viewmodels
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import awais.instagrabber.common.* import awais.instagrabber.common.*
@ -7,19 +8,49 @@ import awais.instagrabber.db.datasources.AccountDataSource
import awais.instagrabber.db.datasources.FavoriteDataSource import awais.instagrabber.db.datasources.FavoriteDataSource
import awais.instagrabber.db.repositories.AccountRepository import awais.instagrabber.db.repositories.AccountRepository
import awais.instagrabber.db.repositories.FavoriteRepository import awais.instagrabber.db.repositories.FavoriteRepository
import awais.instagrabber.getOrAwaitValue
import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User
import awais.instagrabber.webservices.* import awais.instagrabber.webservices.*
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.runner.RunWith import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
internal class ProfileFragmentViewModelTest { internal class ProfileFragmentViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Test @Test
fun testNoUsernameNoCurrentUser() { fun testNoUsernameNoCurrentUser() {
val state = SavedStateHandle(
mutableMapOf<String, Any>(
"username" to ""
)
val accountDataSource = AccountDataSource(AccountDaoAdapter())
val viewModel = ProfileFragmentViewModel(
SavedStateHandle(),
UserRepository(UserServiceAdapter()),
FriendshipRepository(FriendshipServiceAdapter()),
StoriesRepository(StoriesServiceAdapter()),
MediaRepository(MediaServiceAdapter()),
GraphQLRepository(GraphQLServiceAdapter()),
AccountRepository(accountDataSource),
FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter()))
) )
assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())
assertNull(viewModel.profile.getOrAwaitValue().data)
assertEquals("", viewModel.username.getOrAwaitValue())
viewModel.currentUser = Resource.success(null)
assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())
}
@Test
fun testNoUsernameWithCurrentUser() {
// val state = SavedStateHandle(
// mutableMapOf<String, Any?>(
// "username" to "test"
// )
// )
val userRepository = UserRepository(UserServiceAdapter()) val userRepository = UserRepository(UserServiceAdapter())
val friendshipRepository = FriendshipRepository(FriendshipServiceAdapter()) val friendshipRepository = FriendshipRepository(FriendshipServiceAdapter())
val storiesRepository = StoriesRepository(StoriesServiceAdapter()) val storiesRepository = StoriesRepository(StoriesServiceAdapter())
@ -29,7 +60,7 @@ internal class ProfileFragmentViewModelTest {
val accountRepository = AccountRepository(accountDataSource) val accountRepository = AccountRepository(accountDataSource)
val favoriteRepository = FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter())) val favoriteRepository = FavoriteRepository(FavoriteDataSource(FavoriteDaoAdapter()))
val viewModel = ProfileFragmentViewModel( val viewModel = ProfileFragmentViewModel(
state,
SavedStateHandle(),
userRepository, userRepository,
friendshipRepository, friendshipRepository,
storiesRepository, storiesRepository,
@ -38,5 +69,11 @@ internal class ProfileFragmentViewModelTest {
accountRepository, accountRepository,
favoriteRepository favoriteRepository
) )
assertEquals(false, viewModel.isLoggedIn.getOrAwaitValue())
assertNull(viewModel.profile.getOrAwaitValue().data)
val user = User()
viewModel.currentUser = Resource.success(user)
assertEquals(true, viewModel.isLoggedIn.getOrAwaitValue())
assertEquals(user, viewModel.profile.getOrAwaitValue().data)
} }
} }
Loading…
Cancel
Save