How to unit test textChangedStateFlow extension with kotlinx coroutines 1.6.0?

Viewed 369

Consider the following EditText extension:

@FlowPreview
/**
 * Returns a [Flow] representing the text of the [EditText], debounced and repeats filtered out.
 */
fun EditText.textChangedStateFlow(debounceTimeoutMillis: Long): Flow<String> {
    val query = MutableStateFlow("")

    doOnTextChanged { searchTermText, _, _, _ ->
        searchTermText?.let {
            query.value = it.toString()
        }
    }

    return query.drop(1)
        .debounce(debounceTimeoutMillis)
        .distinctUntilChanged()
}

How would I write a unit test to ensure this functionality works? My previous test using coroutines 1.5.2 was setup in this manner:

/**
 * Rule used to allow synchronous execution of coroutines.
 */
@get:Rule
val coroutineTestRule = CoroutineTestRule()

/**
 * The [EditText] to test.
 */
private val editText = EditText(getInstrumentation().targetContext)

@Test
fun textChangedStateFlow() = coroutineTestRule.runBlockingTest {
    val firstText = "Corny!"
    val secondText = "Is cute!"
    var textChangeStateFlowInvokedCount = 0

    coroutineTestRule.testScope.launch {
        editText.textChangedStateFlow(10)
            .collectLatest { text ->
                textChangeStateFlowInvokedCount += 1

                assertEquals(secondText, text)
            }
    }

    editText.setText(firstText)
    editText.setText(secondText)

    coroutineTestRule.advanceUntilIdle()

    assertEquals(1, textChangeStateFlowInvokedCount)
}

Where CoroutineTestRule is:

/**
 * Test rule to help with running coroutines synchronously.
 */
@ExperimentalCoroutinesApi
class CoroutineTestRule : TestWatcher() {
    private val testDispatcher = TestCoroutineDispatcher()
    val testScope = CoroutineScope(testDispatcher)

    override fun starting(description: Description?) {
        super.starting(description)

        Dispatchers.setMain(testDispatcher)
    }

    override fun finished(description: Description?) {
        super.finished(description)

        Dispatchers.resetMain()
        testDispatcher.cleanupTestCoroutines()
        testScope.cancel()
    }

    fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
        testDispatcher.runBlockingTest { block() }
}

With coroutines 1.6.0 however, TestCoroutineDispatcher is deprecated and as far as I can tell I should be using runTest syntax for my tests, not coroutineTestRule.runBlockingTest (which is testDispatcher.runBlockingTest), and I can't get things to work - any guidance with the new library version for this?

0 Answers
Related