es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Prueba unitaria con canal en Kotlin

Empecé a aprender cómo usar el Canal pero tengo problemas con las pruebas unitarias. Por ejemplo con la clase:

class TestOp (private val channel: Channel<string>) {
    suspend fun op() {
        channel.send("new")
    }
}

y para probarlo:

@ExtendWith(CoroutineExecutorExtension::class)
@RunWith(JUnitPlatform::class)
internal class TestOpTest{
    @Test
    fun testOpReturnNew() {
        runBlockingTest {
            val channel = Channel<string>()
            val test = TestOp(channel = channel)
            test.op()
            assertEquals("new", channel.receive())
        }
    }
}

donde:

@ExperimentalCoroutinesApi
class CoroutineExecutorExtension : InstantExecutorExtension() {
    private val testCoroutineDispatcher = TestCoroutineDispatcher()

    override fun beforeEach(context: ExtensionContext?) {
        super.beforeEach(context)
        Dispatchers.setMain(testCoroutineDispatcher)
    }

    override fun afterEach(context: ExtensionContext?) {
        super.afterEach(context)
        Dispatchers.resetMain()
        testCoroutineDispatcher.cleanupTestCoroutines()
    }
}
@ExperimentalCoroutinesApi
open class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
    override fun beforeEach(context: ExtensionContext?) {
        ArchTaskExecutor.getInstance()
            .setDelegate(object : TaskExecutor() {
                override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
                override fun postToMainThread(runnable: Runnable) = runnable.run()
                override fun isMainThread(): Boolean = true
            })
    }

    override fun afterEach(context: ExtensionContext?) {
        ArchTaskExecutor.getInstance().setDelegate(null)
    }
}

Obtengo el error:

This job has not completed yet
java.lang.IllegalStateException: This job has not completed yet
at kotlinx.coroutines.JobSupport.getCompletionExceptionOrNull(JobSupport.kt:1190)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:53)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)

Si uso runBlocking, la prueba nunca se completa.

¿Cuál es la forma correcta de probarlo?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    El problema con la prueba dada es que el canal no se está cerrando correctamente, lo que provoca que la prueba se quede colgada indefinidamente. Para solucionar esto, debemos asegurarnos de que el canal se cierre después de que la prueba haya terminado. Para hacer esto, podemos modificar el código de la prueba de la siguiente manera:

    @ExtendWith(CoroutineExecutorExtension::class)
    @RunWith(JUnitPlatform::class)
    internal class TestOpTest{
    
        @Test
        fun testOpReturnNew() = runBlockingTest {
            val channel = Channel<string>()
            val test = TestOp(channel = channel)
            test.op()
            assertEquals("new", channel.receive())
            channel.close()
        }
    }
    

    Al agregar channel.close() al final de la prueba, nos aseguramos de que el canal se cierre correctamente después de que la prueba haya terminado. Esto debería solucionar el problema de la prueba que se queda colgada indefinidamente.

    Otra cosa a tener en cuenta es que podemos usar la función runBlockingTest directamente en nuestra función de prueba, sin tener que definirla por separado en una extensión. Esto simplifica nuestro código y lo hace más legible.

    @Test
    fun testOpReturnNew() = runBlockingTest {
        val channel = Channel<string>()
        val test = TestOp(channel = channel)
        test.op()
        assertEquals("new", channel.receive())
        channel.close()
    }
    

    Esta debería ser la forma correcta de probar la clase TestOp utilizando canales y corutinas.

Comments are closed.