Document configuration for AGP to unit test Molecule
See original GitHub issueI’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:
- Created a year ago
- Comments:5 (5 by maintainers)
Top 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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Upstream bug about exception message: https://issuetracker.google.com/issues/246752914
Oh, you’re right. I’ll file a bug on AGP for that.