Skip to content
This repository was archived by the owner on Feb 4, 2025. It is now read-only.

Commit 0f7b733

Browse files
authored
Merge pull request #1120 from wordpress-mobile/issue/paged-list-wrapper-unit-tests
PagedListWrapper unit tests
2 parents efa0011 + 377b82f commit 0f7b733

File tree

3 files changed

+209
-0
lines changed

3 files changed

+209
-0
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ ext {
7373
wellSqlVersion = '1.4.0'
7474
supportLibraryVersion = '27.1.1'
7575
arch_paging_version = '1.0.1'
76+
arch_lifecycle_version = '1.1.1'
7677
}

example/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ dependencies {
107107
testImplementation 'org.mockito:mockito-core:2.23.0'
108108
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
109109
testImplementation 'org.assertj:assertj-core:3.11.1'
110+
testImplementation "android.arch.core:core-testing:$arch_lifecycle_version"
110111

111112
androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
112113
androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
package org.wordpress.android.fluxc.list
2+
3+
import android.arch.core.executor.testing.InstantTaskExecutorRule
4+
import android.arch.lifecycle.Lifecycle
5+
import android.arch.lifecycle.LifecycleRegistry
6+
import android.arch.lifecycle.MutableLiveData
7+
import android.arch.lifecycle.Observer
8+
import android.arch.paging.PagedList
9+
import com.nhaarman.mockitokotlin2.firstValue
10+
import com.nhaarman.mockitokotlin2.mock
11+
import com.nhaarman.mockitokotlin2.times
12+
import com.nhaarman.mockitokotlin2.verify
13+
import com.nhaarman.mockitokotlin2.whenever
14+
import org.assertj.core.api.Assertions.assertThat
15+
import org.junit.Rule
16+
import org.junit.Test
17+
import org.junit.runner.RunWith
18+
import org.mockito.ArgumentCaptor
19+
import org.mockito.junit.MockitoJUnitRunner
20+
import org.wordpress.android.fluxc.Dispatcher
21+
import org.wordpress.android.fluxc.model.list.ListDescriptor
22+
import org.wordpress.android.fluxc.model.list.ListDescriptorTypeIdentifier
23+
import org.wordpress.android.fluxc.model.list.ListState
24+
import org.wordpress.android.fluxc.model.list.PagedListItemType
25+
import org.wordpress.android.fluxc.model.list.PagedListWrapper
26+
import org.wordpress.android.fluxc.store.ListStore.ListError
27+
import org.wordpress.android.fluxc.store.ListStore.ListErrorType.GENERIC_ERROR
28+
import org.wordpress.android.fluxc.store.ListStore.ListErrorType.PERMISSION_ERROR
29+
import org.wordpress.android.fluxc.store.ListStore.OnListChanged
30+
import org.wordpress.android.fluxc.store.ListStore.OnListChanged.CauseOfListChange
31+
import org.wordpress.android.fluxc.store.ListStore.OnListChanged.CauseOfListChange.FIRST_PAGE_FETCHED
32+
import org.wordpress.android.fluxc.store.ListStore.OnListItemsChanged
33+
import org.wordpress.android.fluxc.store.ListStore.OnListStateChanged
34+
35+
private fun onlyOnce() = times(1)
36+
37+
@RunWith(MockitoJUnitRunner::class)
38+
class PagedListWrapperTest {
39+
@get:Rule
40+
val rule = InstantTaskExecutorRule()
41+
42+
private val mockLiveData = MutableLiveData<PagedList<PagedListItemType<String>>>()
43+
private val mockDispatcher = mock<Dispatcher>()
44+
private val mockListDescriptor = mock<ListDescriptor>()
45+
private val mockRefresh = mock<() -> Unit>()
46+
private val mockInvalidate = mock<() -> Unit>()
47+
private val mockIsListEmpty = mock<() -> Boolean>()
48+
49+
private fun createPagedListWrapper(lifecycle: Lifecycle = mock()) = PagedListWrapper(
50+
data = mockLiveData,
51+
dispatcher = mockDispatcher,
52+
listDescriptor = mockListDescriptor,
53+
lifecycle = lifecycle,
54+
refresh = mockRefresh,
55+
invalidate = mockInvalidate,
56+
isListEmpty = mockIsListEmpty
57+
)
58+
59+
@Test
60+
fun `registers dispatcher and observes lifecycle in init`() {
61+
val mockLifecycle = mock<Lifecycle>()
62+
63+
val pagedListWrapper = createPagedListWrapper(mockLifecycle)
64+
65+
verify(mockDispatcher, onlyOnce()).register(pagedListWrapper)
66+
verify(mockLifecycle, onlyOnce()).addObserver(pagedListWrapper)
67+
}
68+
69+
@Test
70+
fun `isListEmpty is updated in init`() {
71+
createPagedListWrapper()
72+
73+
verify(mockIsListEmpty, onlyOnce()).invoke()
74+
}
75+
76+
@Test
77+
fun `unregisters dispatcher and stops observing lifecycle on destroy`() {
78+
val lifecycle = LifecycleRegistry(mock())
79+
assertThat(lifecycle.observerCount).isEqualTo(0)
80+
lifecycle.markState(Lifecycle.State.CREATED)
81+
82+
val pagedListWrapper = createPagedListWrapper(lifecycle)
83+
assertThat(lifecycle.observerCount).isEqualTo(1)
84+
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
85+
86+
verify(mockDispatcher, onlyOnce()).register(pagedListWrapper)
87+
verify(mockDispatcher, onlyOnce()).unregister(pagedListWrapper)
88+
assertThat(lifecycle.observerCount).isEqualTo(0)
89+
}
90+
91+
@Test
92+
fun `fetchFirstPage invokes refresh property`() {
93+
val pagedListWrapper = createPagedListWrapper()
94+
95+
pagedListWrapper.fetchFirstPage()
96+
97+
verify(mockRefresh, onlyOnce()).invoke()
98+
}
99+
100+
@Test
101+
fun `invalidateData invokes invalidate property`() {
102+
val pagedListWrapper = createPagedListWrapper()
103+
104+
pagedListWrapper.invalidateData()
105+
106+
verify(mockInvalidate, onlyOnce()).invoke()
107+
}
108+
109+
@Test
110+
fun `fetchingFirstPage ListState is propagated correctly`() {
111+
testListStateIsPropagatedCorrectly(ListState.FETCHING_FIRST_PAGE)
112+
}
113+
114+
@Test
115+
fun `loadingMore ListState is propagated correctly`() {
116+
testListStateIsPropagatedCorrectly(ListState.LOADING_MORE)
117+
}
118+
119+
@Test
120+
fun `default ListState is propagated correctly`() {
121+
testListStateIsPropagatedCorrectly(ListState.defaultState)
122+
}
123+
124+
@Test
125+
fun `permission ListError is propagated correctly`() {
126+
testListStateIsPropagatedCorrectly(ListState.ERROR, ListError(PERMISSION_ERROR))
127+
}
128+
129+
@Test
130+
fun `generic ListError is propagated correctly`() {
131+
testListStateIsPropagatedCorrectly(ListState.ERROR, ListError(GENERIC_ERROR))
132+
}
133+
134+
@Test
135+
fun `onListChanged invokes invalidate property`() {
136+
triggerOnListChanged()
137+
verify(mockInvalidate, onlyOnce()).invoke()
138+
}
139+
140+
@Test
141+
fun `onListChanged invokes updates isEmpty`() {
142+
triggerOnListChanged()
143+
// PagedListWrapper.init will trigger `isEmpty` once
144+
verify(mockIsListEmpty, times(2)).invoke()
145+
}
146+
147+
@Test
148+
fun `onListItemsChanged invokes invalidate property`() {
149+
triggerOnListItemsChanged()
150+
verify(mockInvalidate, onlyOnce()).invoke()
151+
}
152+
153+
@Test
154+
fun `onListItemsChanged invokes updates isEmpty`() {
155+
triggerOnListItemsChanged()
156+
// PagedListWrapper.init will trigger `isEmpty` once
157+
verify(mockIsListEmpty, times(2)).invoke()
158+
}
159+
160+
private fun testListStateIsPropagatedCorrectly(listState: ListState, listError: ListError? = null) {
161+
val pagedListWrapper = createPagedListWrapper()
162+
val isFetchingFirstPageObserver = mock<Observer<Boolean>>()
163+
val isLoadingMoreObserver = mock<Observer<Boolean>>()
164+
val listErrorObserver = mock<Observer<ListError?>>()
165+
pagedListWrapper.isFetchingFirstPage.observeForever(isFetchingFirstPageObserver)
166+
pagedListWrapper.isLoadingMore.observeForever(isLoadingMoreObserver)
167+
pagedListWrapper.listError.observeForever(listErrorObserver)
168+
169+
val event = OnListStateChanged(mockListDescriptor, listState, listError)
170+
pagedListWrapper.onListStateChanged(event)
171+
172+
captureAndVerifySingleValue(isFetchingFirstPageObserver, listState.isFetchingFirstPage())
173+
captureAndVerifySingleValue(isLoadingMoreObserver, listState.isLoadingMore())
174+
captureAndVerifySingleValue(listErrorObserver, listError)
175+
}
176+
177+
private inline fun <reified T> captureAndVerifySingleValue(observer: Observer<T>, result: T) {
178+
val captor = ArgumentCaptor.forClass(T::class.java)
179+
verify(observer, onlyOnce()).onChanged(captor.capture())
180+
assertThat(captor.firstValue).isEqualTo(result)
181+
}
182+
183+
private fun triggerOnListChanged(
184+
causeOfListChange: CauseOfListChange = FIRST_PAGE_FETCHED,
185+
error: ListError? = null
186+
) {
187+
val pagedListWrapper = createPagedListWrapper()
188+
val event = OnListChanged(
189+
listDescriptors = listOf(mockListDescriptor),
190+
causeOfChange = causeOfListChange,
191+
error = error
192+
)
193+
pagedListWrapper.onListChanged(event)
194+
}
195+
196+
private fun triggerOnListItemsChanged(
197+
error: ListError? = null
198+
) {
199+
val pagedListWrapper = createPagedListWrapper()
200+
whenever(mockListDescriptor.typeIdentifier).thenReturn(ListDescriptorTypeIdentifier(0))
201+
val event = OnListItemsChanged(
202+
type = mockListDescriptor.typeIdentifier,
203+
error = error
204+
)
205+
pagedListWrapper.onListItemsChanged(event)
206+
}
207+
}

0 commit comments

Comments
 (0)