Skip to content

Flaky tests after migration to GitHubAction #349

Description

@Onvistlex

First, thanks for this nice library :)

We have a lot of unit tests that we've been running locally without any problems in the past. However, after migrating these tests to a GitHub Action, we noticed that some of the tested StateFlows were collapsing multiple expected states into one, which resulted in assertion errors. It took me a while to reproduce this locally as a minimal working example. It seems that the sheer number of tests is causing this behavior:

@Suppress("OPT_IN_USAGE")
class ExampleUnitTest {

    init { Dispatchers.setMain(UnconfinedTestDispatcher()) }

    @Test
    fun `tests if success is emitted`() = runTest {
        repeat(2000) {
            SimpleViewModel().stateFlow.test {
                assertEquals("Run count: $it", "Loading", awaitItem())
                assertEquals("Run count: $it", "Success", awaitItem())
                cancelAndIgnoreRemainingEvents()
            }
        }
    }
}

suspend fun read3rdPartyData(): String = withContext(Dispatchers.IO) { "Success" }

internal class SimpleViewModel : ViewModel() {

    val stateFlow = flow {
        emit("Loading")
        emit(read3rdPartyData())
    }.catch {
        emit("Error")
    }.stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5000),
        initialValue = "Loading"
    )
}

Because many of our tests are similar to this example, we wrote a more sophisticated awaitItem() function that can skip states that are not of interest (e.g., skipping the loading state when checking for the success state):

/**
 * Skips items of other types until the wanted item type is received and its predicate passes.
 */
suspend inline fun <reified T> ReceiveTurbine<in T>.awaitItemType(
    predicate: (T) -> Boolean = { true }
): T {
    var item = awaitItem()
    var predicatePassed = (item as? T)?.let(predicate) ?: false
    while (item !is T || predicatePassed.not()) {
        item = awaitItem()
        predicatePassed = (item as? T)?.let(predicate) ?: false
    }
    return item
}

Would it be possible to add a similar function to Turbine? Or did we miss something in our test setup? Any help would be appreciated! 😊

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions