Merge pull request #607 from android/mv/tests

Provide a CoroutineScope to fake DataStores
pull/622/head
Don Turner 2 years ago committed by GitHub
commit 6d1f85b8ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -36,6 +36,8 @@ import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@ -45,6 +47,8 @@ import kotlin.test.assertEquals
class OfflineFirstNewsRepositoryTest {
private val testScope = TestScope(UnconfinedTestDispatcher())
private lateinit var subject: OfflineFirstNewsRepository
private lateinit var newsResourceDao: TestNewsResourceDao
@ -65,7 +69,7 @@ class OfflineFirstNewsRepositoryTest {
network = TestNiaNetworkDataSource()
synchronizer = TestSynchronizer(
NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore(),
tmpFolder.testUserPreferencesDataStore(testScope),
),
)
@ -78,7 +82,7 @@ class OfflineFirstNewsRepositoryTest {
@Test
fun offlineFirstNewsRepository_news_resources_stream_is_backed_by_news_resource_dao() =
runTest {
testScope.runTest {
assertEquals(
newsResourceDao.getNewsResources()
.first()
@ -90,7 +94,7 @@ class OfflineFirstNewsRepositoryTest {
@Test
fun offlineFirstNewsRepository_news_resources_for_topic_is_backed_by_news_resource_dao() =
runTest {
testScope.runTest {
assertEquals(
expected = newsResourceDao.getNewsResources(
filterTopicIds = filteredInterestsIds,
@ -119,7 +123,7 @@ class OfflineFirstNewsRepositoryTest {
@Test
fun offlineFirstNewsRepository_sync_pulls_from_network() =
runTest {
testScope.runTest {
subject.syncWith(synchronizer)
val newsResourcesFromNetwork = network.getNewsResources()
@ -144,7 +148,7 @@ class OfflineFirstNewsRepositoryTest {
@Test
fun offlineFirstNewsRepository_sync_deletes_items_marked_deleted_on_network() =
runTest {
testScope.runTest {
val newsResourcesFromNetwork = network.getNewsResources()
.map(NetworkNewsResource::asEntity)
.map(NewsResourceEntity::asExternalModel)
@ -185,7 +189,7 @@ class OfflineFirstNewsRepositoryTest {
@Test
fun offlineFirstNewsRepository_incremental_sync_pulls_from_network() =
runTest {
testScope.runTest {
// Set news version to 7
synchronizer.updateChangeListVersions {
copy(newsResourceVersion = 7)
@ -224,7 +228,7 @@ class OfflineFirstNewsRepositoryTest {
@Test
fun offlineFirstNewsRepository_sync_saves_shell_topic_entities() =
runTest {
testScope.runTest {
subject.syncWith(synchronizer)
assertEquals(
@ -239,7 +243,7 @@ class OfflineFirstNewsRepositoryTest {
@Test
fun offlineFirstNewsRepository_sync_saves_topic_cross_references() =
runTest {
testScope.runTest {
subject.syncWith(synchronizer)
assertEquals(

@ -29,6 +29,8 @@ import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferen
import com.google.samples.apps.nowinandroid.core.model.data.Topic
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@ -38,6 +40,8 @@ import kotlin.test.assertEquals
class OfflineFirstTopicsRepositoryTest {
private val testScope = TestScope(UnconfinedTestDispatcher())
private lateinit var subject: OfflineFirstTopicsRepository
private lateinit var topicDao: TopicDao
@ -56,7 +60,7 @@ class OfflineFirstTopicsRepositoryTest {
topicDao = TestTopicDao()
network = TestNiaNetworkDataSource()
niaPreferences = NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore(),
tmpFolder.testUserPreferencesDataStore(testScope),
)
synchronizer = TestSynchronizer(niaPreferences)
@ -68,7 +72,7 @@ class OfflineFirstTopicsRepositoryTest {
@Test
fun offlineFirstTopicsRepository_topics_stream_is_backed_by_topics_dao() =
runTest {
testScope.runTest {
assertEquals(
topicDao.getTopicEntities()
.first()
@ -80,7 +84,7 @@ class OfflineFirstTopicsRepositoryTest {
@Test
fun offlineFirstTopicsRepository_sync_pulls_from_network() =
runTest {
testScope.runTest {
subject.syncWith(synchronizer)
val networkTopics = network.getTopics()
@ -103,7 +107,7 @@ class OfflineFirstTopicsRepositoryTest {
@Test
fun offlineFirstTopicsRepository_incremental_sync_pulls_from_network() =
runTest {
testScope.runTest {
// Set topics version to 10
synchronizer.updateChangeListVersions {
copy(topicVersion = 10)
@ -133,7 +137,7 @@ class OfflineFirstTopicsRepositoryTest {
@Test
fun offlineFirstTopicsRepository_sync_deletes_items_marked_deleted_on_network() =
runTest {
testScope.runTest {
val networkTopics = network.getTopics()
.map(NetworkTopic::asEntity)
.map(TopicEntity::asExternalModel)

@ -24,6 +24,8 @@ import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand
import com.google.samples.apps.nowinandroid.core.model.data.UserData
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@ -34,6 +36,9 @@ import kotlin.test.assertFalse
import kotlin.test.assertTrue
class OfflineFirstUserDataRepositoryTest {
private val testScope = TestScope(UnconfinedTestDispatcher())
private lateinit var subject: OfflineFirstUserDataRepository
private lateinit var niaPreferencesDataSource: NiaPreferencesDataSource
@ -46,7 +51,7 @@ class OfflineFirstUserDataRepositoryTest {
@Before
fun setup() {
niaPreferencesDataSource = NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore(),
tmpFolder.testUserPreferencesDataStore(testScope),
)
subject = OfflineFirstUserDataRepository(
@ -57,7 +62,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_default_user_data_is_correct() =
runTest {
testScope.runTest {
assertEquals(
UserData(
bookmarkedNewsResources = emptySet(),
@ -73,7 +78,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_toggle_followed_topics_logic_delegates_to_nia_preferences() =
runTest {
testScope.runTest {
subject.toggleFollowedTopicId(followedTopicId = "0", followed = true)
assertEquals(
@ -104,7 +109,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_set_followed_topics_logic_delegates_to_nia_preferences() =
runTest {
testScope.runTest {
subject.setFollowedTopicIds(followedTopicIds = setOf("1", "2"))
assertEquals(
@ -126,7 +131,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_bookmark_news_resource_logic_delegates_to_nia_preferences() =
runTest {
testScope.runTest {
subject.updateNewsResourceBookmark(newsResourceId = "0", bookmarked = true)
assertEquals(
@ -157,7 +162,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_set_theme_brand_delegates_to_nia_preferences() =
runTest {
testScope.runTest {
subject.setThemeBrand(ThemeBrand.ANDROID)
assertEquals(
@ -177,7 +182,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_set_dynamic_color_delegates_to_nia_preferences() =
runTest {
testScope.runTest {
subject.setDynamicColorPreference(true)
assertEquals(
@ -197,7 +202,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun offlineFirstUserDataRepository_set_dark_theme_config_delegates_to_nia_preferences() =
runTest {
testScope.runTest {
subject.setDarkThemeConfig(DarkThemeConfig.DARK)
assertEquals(
@ -217,7 +222,7 @@ class OfflineFirstUserDataRepositoryTest {
@Test
fun whenUserCompletesOnboarding_thenRemovesAllInterests_shouldHideOnboardingIsFalse() =
runTest {
testScope.runTest {
subject.setFollowedTopicIds(setOf("1"))
subject.setShouldHideOnboarding(true)
assertTrue(subject.userData.first().shouldHideOnboarding)

@ -24,6 +24,7 @@ android {
dependencies {
api(project(":core:datastore"))
implementation(project(":core:common"))
implementation(project(":core:testing"))
api(libs.androidx.dataStore.core)

@ -21,10 +21,15 @@ import androidx.datastore.core.DataStoreFactory
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer
import com.google.samples.apps.nowinandroid.core.datastore.di.DataStoreModule
import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import dagger.Module
import dagger.Provides
import dagger.hilt.components.SingletonComponent
import dagger.hilt.testing.TestInstallIn
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import org.junit.rules.TemporaryFolder
import javax.inject.Singleton
@ -38,16 +43,23 @@ object TestDataStoreModule {
@Provides
@Singleton
fun providesUserPreferencesDataStore(
@Dispatcher(IO) ioDispatcher: CoroutineDispatcher,
userPreferencesSerializer: UserPreferencesSerializer,
tmpFolder: TemporaryFolder,
): DataStore<UserPreferences> =
tmpFolder.testUserPreferencesDataStore(userPreferencesSerializer)
tmpFolder.testUserPreferencesDataStore(
// TODO: Provide an application-wide CoroutineScope in the DI graph
coroutineScope = CoroutineScope(SupervisorJob() + ioDispatcher),
userPreferencesSerializer = userPreferencesSerializer,
)
}
fun TemporaryFolder.testUserPreferencesDataStore(
coroutineScope: CoroutineScope,
userPreferencesSerializer: UserPreferencesSerializer = UserPreferencesSerializer(),
) = DataStoreFactory.create(
serializer = userPreferencesSerializer,
scope = coroutineScope,
) {
newFile("user_preferences_test.pb")
}

@ -18,6 +18,8 @@ package com.google.samples.apps.nowinandroid.core.datastore
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
@ -27,6 +29,9 @@ import kotlin.test.assertFalse
import kotlin.test.assertTrue
class NiaPreferencesDataSourceTest {
private val testScope = TestScope(UnconfinedTestDispatcher())
private lateinit var subject: NiaPreferencesDataSource
@get:Rule
@ -35,54 +40,56 @@ class NiaPreferencesDataSourceTest {
@Before
fun setup() {
subject = NiaPreferencesDataSource(
tmpFolder.testUserPreferencesDataStore(),
tmpFolder.testUserPreferencesDataStore(testScope),
)
}
@Test
fun shouldHideOnboardingIsFalseByDefault() = runTest {
fun shouldHideOnboardingIsFalseByDefault() = testScope.runTest {
assertFalse(subject.userData.first().shouldHideOnboarding)
}
@Test
fun userShouldHideOnboardingIsTrueWhenSet() = runTest {
fun userShouldHideOnboardingIsTrueWhenSet() = testScope.runTest {
subject.setShouldHideOnboarding(true)
assertTrue(subject.userData.first().shouldHideOnboarding)
}
@Test
fun userShouldHideOnboarding_unfollowsLastTopic_shouldHideOnboardingIsFalse() = runTest {
// Given: user completes onboarding by selecting a single topic.
subject.toggleFollowedTopicId("1", true)
subject.setShouldHideOnboarding(true)
fun userShouldHideOnboarding_unfollowsLastTopic_shouldHideOnboardingIsFalse() =
testScope.runTest {
// Given: user completes onboarding by selecting a single topic.
subject.toggleFollowedTopicId("1", true)
subject.setShouldHideOnboarding(true)
// When: they unfollow that topic.
subject.toggleFollowedTopicId("1", false)
// When: they unfollow that topic.
subject.toggleFollowedTopicId("1", false)
// Then: onboarding should be shown again
assertFalse(subject.userData.first().shouldHideOnboarding)
}
// Then: onboarding should be shown again
assertFalse(subject.userData.first().shouldHideOnboarding)
}
@Test
fun userShouldHideOnboarding_unfollowsAllTopics_shouldHideOnboardingIsFalse() = runTest {
// Given: user completes onboarding by selecting several topics.
subject.setFollowedTopicIds(setOf("1", "2"))
subject.setShouldHideOnboarding(true)
fun userShouldHideOnboarding_unfollowsAllTopics_shouldHideOnboardingIsFalse() =
testScope.runTest {
// Given: user completes onboarding by selecting several topics.
subject.setFollowedTopicIds(setOf("1", "2"))
subject.setShouldHideOnboarding(true)
// When: they unfollow those topics.
subject.setFollowedTopicIds(emptySet())
// When: they unfollow those topics.
subject.setFollowedTopicIds(emptySet())
// Then: onboarding should be shown again
assertFalse(subject.userData.first().shouldHideOnboarding)
}
// Then: onboarding should be shown again
assertFalse(subject.userData.first().shouldHideOnboarding)
}
@Test
fun shouldUseDynamicColorFalseByDefault() = runTest {
fun shouldUseDynamicColorFalseByDefault() = testScope.runTest {
assertFalse(subject.userData.first().useDynamicColor)
}
@Test
fun userShouldUseDynamicColorIsTrueWhenSet() = runTest {
fun userShouldUseDynamicColorIsTrueWhenSet() = testScope.runTest {
subject.setDynamicColorPreference(true)
assertTrue(subject.userData.first().useDynamicColor)
}

Loading…
Cancel
Save