I have a `stateFlow` that combines two `snapshotFl...
# flow
z
I have a
stateFlow
that combines two
snapshotFlow
that works when running on device, but when I attempt to unit test with turbine, it never emits more than once. Am I doing something wrong or am I going down the wrong path?
Copy code
fun validInput() = runTest {
    viewModel.screenState.test {
        assertEquals(awaitItem(), EnterNameScreenState.EnterNameLoadingResults) // passes
        viewModel.firstNameInputValidation("test")
        // fails, waited too long
        assertEquals(awaitItem(), EnterNameScreenState.EnterNameLoadedResults(InputWrapper(value = "test"), InputWrapper(), false)). 
    }
}
ViewModel:
Copy code
private var firstNameState by savedStateHandle.saveable { mutableStateOf(InputWrapper()) }
private var lastNameState by savedStateHandle.saveable { mutableStateOf(InputWrapper()) }

val screenState: StateFlow<EnterNameScreenState> = combine(
    snapshotFlow { firstNameState },
    snapshotFlow { lastNameState },
) { firstName, lastName ->
    flowOf<EnterNameScreenState>(
        EnterNameScreenState.EnterNameLoadedResults(
            firstName = firstName,
            lastName = lastName,
            continueButtonEnabled = isContinueEnabled(firstName, lastName)
        )
    )
}
    .flatMapLatest { it }
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000L),
        initialValue = EnterNameScreenState.EnterNameLoadingResults
    )
m
testing hot flows like
StateFlow
is a little bit different from testing cold flows, you have to explicitly emit new values inside your
test { ... }
and in turn it would be collected by turbine's channel, and received from
awaitItem()
. take a look at https://github.com/cashapp/turbine#hot-flows
z
Can I call outside functions (like in a viewmodel) that would emit or cause something to emit? Or do I literally have to call
emit
inside the test block?
Honestly after banging my head on this for a few hours it seems to be a
snapshotFlow
thing.
m
any call that eventually
emit
a new value should just work
z
Okay, I figured out I did not have the right test dispatcher set up so I created an extension for it and that worked
k
Hi @Zach, are you able to share what type of test dispatcher you had to use. I’m also running into issues with testing hot flows that I can seem to figure out
In my case, I’m trying to test a sharedflow that receives multiple subscriptions within the test with the expectation that the old subscribers get the newer emissions triggered based off each of the newer subscriptions but I only see emissions being delivered to a single subscriber which happens to be the latest subscribed at the time
z
@Keith Mayoral Here is my test dispatcher extension
Copy code
class MainDispatcherExtension(
    val testDispatcher: TestDispatcher = UnconfinedTestDispatcher(),
) : AfterEachCallback, BeforeEachCallback {
    override fun beforeEach(context: ExtensionContext?) {
        Dispatchers.setMain(testDispatcher)
    }

    override fun afterEach(context: ExtensionContext?) {
        Dispatchers.resetMain()
    }
}
I am not sure about the multiple subscriber case, I still haven’t tested that
433 Views