question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Document configuration for AGP to unit test Molecule

See original GitHub issue

I’m trying out Molecule with AAC ViewModel. Here is how the VM code is written exposing stateFlow stream using launchMolecule()

Molecule version used : 0.5.0-SNAPSHOT

@HiltViewModel
class PhotosListViewModel @Inject constructor(
    private val unsplashRepository: UnsplashRepository,
    @MoleculeScope private val scope: CoroutineScope,
    @CompositionClock private val clock: RecompositionClock,
) : ViewModel() {

    private val events = Channel<Event>()
    val stateFlow = scope.launchMolecule(clock = clock) {
        present(events.receiveAsFlow())
    }

    init {
        processEvent(InitialPageEvent)
    }

    @Composable
    fun present(events: Flow<Event>): PhotosListUIState {
        var isLoading by remember { mutableStateOf(false) }
        var error by remember { mutableStateOf<String?>(null) }
        val images = remember { mutableStateListOf<UnsplashImage>() }

        LaunchedEffect(Unit) {
            events.collect { event ->
                when (event) {
                    InitialPageEvent -> {
                        isLoading = true
                        error = null
                        when (val result =
                            unsplashRepository.getPhotos(page = 1, perPage = ITEM_PER_PAGE)) {
                            is Error -> {
                                isLoading = false
                                error = result.message
                            }
                            is Success -> {
                                isLoading = false
                                images.addAll(result.data.images)
                            }
                        }

                    }
                }
            }
        }
        return PhotosListUIState(
            isLoading = isLoading,
            error = error,
            images = images,
        )
    }

    fun processEvent(event: Event) {
        scope.launch {
            events.send(event)
        }
    }
}

// UI State
data class PhotosListUIState(
    val isLoading: Boolean = false,
    val error: String? = null,
    val images: List<UnsplashImage> = emptyList(),
)

// Events
sealed interface Event
object InitialPageEvent : Event

App works fine on Android devices without any issue when VM is provided with correct coroutineScope and frameClock. However, when I write Unit test for VM it requires android.os.Trace to be mocked. Added both unit test and it’s error log below.

VM’s Unit Test

import app.cash.molecule.RecompositionClock
import app.cash.turbine.testIn
import dev.punitd.unplashapp.data.fake.FakeUnsplashRepository
import dev.punitd.unplashapp.model.images
import dev.punitd.unplashapp.model.pageLinks
import dev.punitd.unplashapp.util.CoroutineRule
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test


@OptIn(ExperimentalCoroutinesApi::class)
class PhotosListViewModelTest {

    @get:Rule
    val coroutineRule = CoroutineRule()

    @Test
    fun successStateIfApiSucceeds() = runTest {
        val viewModel = PhotosListViewModel(
            unsplashRepository = FakeUnsplashRepository(coroutineRule.testDispatcher),
            scope = this, // TestScope
            clock = RecompositionClock.Immediate,
        )

        // InitialPageEvent is send inside VM's init{} block by default
        // That's why we're not sending any events here.

        val turbine = viewModel.stateFlow.testIn(this)
        assertEquals(PhotosListUIState(isLoading = false), turbine.awaitItem())
        assertEquals(PhotosListUIState(isLoading = true), turbine.awaitItem())
        assertEquals(
            PhotosListUIState(
                isLoading = false,
                error = null,
                images = images,
                pageLinks = pageLinks
            ),
            turbine.awaitItem()
        )
        turbine.cancel()
    }
}

Test Error log

Method beginSection in android.os.Trace not mocked. See http://g.co/androidstudio/not-mocked for details.
java.lang.RuntimeException: Method beginSection in android.os.Trace not mocked. See http://g.co/androidstudio/not-mocked for details.
	at android.os.Trace.beginSection(Trace.java)
	at androidx.compose.runtime.Trace.beginSection(ActualAndroid.android.kt:30)
	at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:4357)
	at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3119)
	at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:584)
	at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:811)
	at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:519)
	at app.cash.molecule.MoleculeKt.launchMolecule(molecule.kt:163)
	at app.cash.molecule.MoleculeKt.launchMolecule(molecule.kt:108)
	at dev.punitd.unplashapp.screen.photos.PhotosListViewModel.<init>(PhotosListViewModel.kt:32)

I’m sure i’m doing something stupid here because unit tests written in sample app of this repo works just fine without any such error.

Questions:

  • Is there something I need to mock which i’m missing here?
  • Are Coroutinescope and FrameClock passed to VM in test incorrect?

Repo to reproduce it : https://github.com/punitda/Tinysplash

Thanks!

Issue Analytics

  • State:closed
  • Created a year ago
  • Comments:5 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
JakeWhartoncommented, Sep 14, 2022

Upstream bug about exception message: https://issuetracker.google.com/issues/246752914

1reaction
JakeWhartoncommented, Sep 14, 2022

Oh, you’re right. I’ll file a bug on AGP for that.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Release None AUTHORS.rst - Molecule Documentation
Molecule is designed to aid in the development and testing of Ansible roles including support for multiple instances,.
Read more >
Molecule - Build a StateFlow stream using Jetpack Compose
If you're unit testing Molecule on the JVM in an Android module, please set below in your project's AGP config. android { ......
Read more >
About Ansible Molecule — Molecule Documentation
Molecule project is designed to aid in the development and testing of Ansible roles. Molecule provides support for testing with multiple instances, ...
Read more >
Advanced test setup - Android Developers
Test in Android Studio and Test from the command line explain how to set up and run basic test configurations. However, when your...
Read more >
How To Test Ansible Roles with Molecule on Ubuntu 16.04
With your test file configured to use the Ansible backend, let's write unit tests to test the state of the host. The first...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found