Skip to content

Flaky tests after migration to GitHubAction #349

@Onvistlex

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions