wajahatkarim3 / Imagine

An simple image gallery app utilizing Unsplash API to showcase modern Android development architecture (MVVM + Kotlin + Retrofit2 + Hilt + Coroutines + Kotlin Flow + mockK + Espresso + Junit)
Apache License 2.0
361 stars 61 forks source link

[Question]: PagingData<ModelName> instead of List<ModelName> #18

Open Drjacky opened 3 years ago

Drjacky commented 3 years ago

What if the mocked data is PagingData? What should we write here: https://github.com/wajahatkarim3/Imagine/blob/6c74f255d82936941cf023efc352d790a8865681/app/src/test/java/com/wajahatkarim3/imagine/ui/home/HomeViewModelTest.kt#L69

wajahatkarim3 commented 3 years ago

Are you referring to Paging 2 or 3? I tried this with Paging 2 but my tests couldn't pass. So I will change the implementation to Paging 3 in a separate branch and also update the tests as well.

Drjacky commented 3 years ago

@wajahatkarim3 I'm referring to Paging 3. This is my test:

@RunWith(JUnit4::class)
class ProductsListViewModelTest {

    private lateinit var viewModel: ProductsListViewModel

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @ExperimentalCoroutinesApi
    @get:Rule
    var coroutineRule = MainCoroutineRule()

    @MockK
    lateinit var getBeersListUseCase: GetBeersListUseCase

    @MockK
    lateinit var getBeersListByCoroutineUseCase: GetBeersListByCoroutineUseCase

    @MockK
    lateinit var savedStateHandle: SavedStateHandle

    @Before
    fun setUp() {
        MockKAnnotations.init(this)
    }

    @After
    fun tearDown() {
    }

    @ExperimentalCoroutinesApi
    @Test
    fun `test when ProductsListViewModel is initialized, products are fetched`() =
        coroutineRule.testDispatcher.runBlockingTest {
            // Given
            val givenProducts = ProductUIFactory.createProducts(3)
            val flow = flow<PagingData<RecyclerItem>> {
//                emit(LoadState.Loading)
                delay(10)
                emit(PagingData.from(givenProducts))
            }
            val productsListObserver = mockk<Observer<PagingData<RecyclerItem>>>(relaxed = true)

            // When
            coEvery {
                getBeersListUseCase.invoke(GetBeersListParams(anyString()))
                getBeersListByCoroutineUseCase.invoke(GetBeersListByCoroutineParams(anyString()))
            }
                .returns(flow)

            // Invoke
            every {
                savedStateHandle.get<ChoosePathType>(CHOOSE_PATH_TYPE) ?: ChoosePathType.COROUTINE
            } returns ChoosePathType.COROUTINE
            viewModel = ProductsListViewModel(
                getBeersUseCase = getBeersListUseCase,
                getBeersListByCoroutineUseCase = getBeersListByCoroutineUseCase,
                savedStateHandle = savedStateHandle
            )
            viewModel.productsListByCoroutine.asLiveData().observeForever(productsListObserver)

            // Then
            coroutineRule.advanceTimeBy(10)
            coVerify(exactly = 1) {
                getBeersListByCoroutineUseCase.invoke(
                    GetBeersListByCoroutineParams(anyString())
                )
            }
            //verify { productsListObserver.onChanged(match { it == PagingData.from(givenProducts) }) }
            verify {
                productsListObserver.onChanged(match { it != null })
            }
            verify(exactly = 1) {
                productsListObserver.onChanged(match { it != null })
            }
            /*val expectedFlow = flow<PagingData<RecyclerItem>> {
                emit(PagingData.from(givenProducts))
            }*/

//            assertNotNull(flow)
//            verify { productsListObserver.onChanged(match { it == expectedFlow }) }
        }

}

But, not sure what to write in the onChanged(match { scope.

wajahatkarim3 commented 3 years ago

Thank you for sharing the code snippet. I will add the Paging 3 API and see how it works for the tests.

Drjacky commented 2 years ago

@wajahatkarim3 Have you found the solution for this?