Kotlin - Test

  • Les proves unitàries són petites proves aïllades que comproven si un mètode, classe, funcionalitat o component implementa correctament la seva lògica empresarial.

    Introducció

    Hello World

    Crea un projecte amb Amper

    test/

    El codi de proves es troba a la carpeta test/:

    |-src/ # production code
    | ...
    |-test/ # test code
    | |-MainTest.kt
    | |-...
    |-module.yaml

    Amper configura el framework de proves Kotlin test de manera automàtica per a cada plataforma

    L’únic que has de fer és afegir els teus tests en la carpeta test/.

    WorldTest

    En crear el projecte s’ha creat per defecte la classe de proves WorldTest.

    Les funcions doTest i shoulFail estan anotades amb @Test.

    Aquesta anotació indica al framework de proves que es tracta d’una prova que ha d’executar.

    Una de les proves passa i l’altra falla 🤔

    És important confirmar que una prova falla si ha de fallar!

    Activitat

    Modifica la funció shouldFail per tal que passi la prova.

    Test-Driven Development

    Crea el fitxer test/CalculatorTest.kt amb una prova per a la funció add() que suma dos números,

    test/CalculatorTest.kt
    import Calculator.add
    import kotlin.test.Test
    import kotlin.test.assertEquals
    class CalculatorTest {
    @Test
    fun testAdd() {
    assertEquals(5, add(2, 3))
    }
    }

    Ja sé que la IDE diu que no pot resoldre la referència add i que el primer que penses a fer és crear la funció add, però ara l’objectiu és aprendre a programar quan les coses són molt òbvies.

    Apren una metodologia que et servirà quan les coses no siguin obvies ⛈️🌩️ 😐

    El test falla! (A vegades no passa 😹, a mi més d’una vegada, com més anys més històries per explicar).

    I a més tens una explicació que coincideix amb el que segur que pensaves, que bé 👻.

    Ara el primer objectiu és que el codi funcioni.

    Has de crear la referència add en el fitxer src/Calculator.kt

    src/Calculator.kt
    fun add(a: Int, b: Int) = 0

    Ara si executes el test, l’error és diferent, però el programa funciona!! 😺

    Nota

    Et pot semblar una mica absurd el que estem fent, però l’art de la programació consisteix en poder avançar en passos molt petits quan és necessari.

    🌞⭐🌟

    Ara hem d’avançar un pas més, ¿Que et sembla aquesta solució?

    src/Calculator.kt
    fun add(a: Int, b: Int) = 5

    Ara el test passa!

    Però que la funció add() passi el test testAdd() no vol dir que estigui ben implementada.

    A l’hora de dissenyar els tests has de preveure tot el que no pot anar bé:

    El que has de fer a continuació és afegir una altra prova per verificar que la funció add funciona correctament.

    test/CalculatorTest.kt
    import Calculator.add
    import kotlin.test.Test
    import kotlin.test.assertEquals
    @Test
    fun testAdd() {
    assertEquals(5, add(2, 3))
    assertEquals(10, add(5, 5))
    }

    I pots veure que no està ben implementada:

    org.opentest4j.AssertionFailedError: expected: <10> but was: <5>
    Expected :10
    Actual :5
    Activitat

    Modifica la funció add per tal que passi el test, i només aquest test!

    Encara que una funció passi un conjunt de proves no vol dir que estigui ben implementada!

    Activitat

    Assertions

    La biblioteca kotlin.test proporciona un conjunt de funcions que pots utilitzar per validar el comportament del teu codi.

    assertEqualsAsserts that the expected value is equal to the actual value
    assertNotEquals
    assertTrueAssert that …
    assertFalseAssert that …
    assertNullAssert that …
    assertNotNullAssert that …

    assertFailsWith

    La biblioteca estàndard de Kotlin proporciona una funció per provar excepcions.

    Pots utilitzar el mètode assertFailsWith per comprovar que un bloc de codi falla amb un tipus d’excepció.

    A més, també pots definir un missatge com a paràmetre de la funció.

    El missatge és opcional i s’utilitza com a prefix del missatge d’error només quan falla l’afirmació:

    test/CalculatorTest.kt
    import kotlin.test.assertFailsWith
    @Test
    fun indexOutOfBounds() {
    assertFailsWith<ArrayIndexOutOfBoundsException>(
    message = "No exception found",
    block = {
    val list = listOf(1, 2, 3)
    list[5]
    }
    )
    }

    Dependències

    Les dependències addicionals només per a proves s’han d’afegir a la secció test-dependencies: del fitxer de configuració del mòdul.

    Imagina’t que estas desenvolupant un servidor amb Ktor.

    Has de configurar el mòdul amb les dependències corresponents:

    module.yaml
    product: jvm/app
    # aquestes dependències estan disponibles en el codi principal i de proves
    dependencies:
    - io.ktor:ktor-server-core:3.2.0
    - io.ktor:ktor-server-netty:3.2.0
    # dependències addicionals per al codi de proves
    test-dependencies:
    - io.ktor:ktor-server-test-host:3.2.0

    A continuació pots implementar un servidor amb un endpoint GET a la ruta /:

    src/main.kt
    import io.ktor.server.application.*
    import io.ktor.server.engine.embeddedServer
    import io.ktor.server.netty.Netty
    import io.ktor.server.response.*
    import io.ktor.server.routing.*
    fun main(args: Array<String>) {
    embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true)
    }
    fun Application.module() {
    routing {
    get("/") {
    call.respondText("Hello, world!")
    }
    }
    }

    Pots arrancar el servidor:

    Terminal window
    .\amper run

    I provar que funciona obrint el navegador a http://localhost:8080:

    O pots implementar una prova per automatitzar la verificació de la implementació del projecte:

    test/ApplicationTest.kt
    import io.ktor.client.request.*
    import io.ktor.client.statement.*
    import io.ktor.http.*
    import io.ktor.server.testing.*
    import kotlin.test.*
    class ApplicationTest {
    @Test
    fun testRoot() = testApplication {
    application {
    module()
    }
    val response = client.get("/")
    assertEquals(HttpStatusCode.OK, response.status)
    assertEquals("Hello, world!", response.bodyAsText())
    }
    }

    I verificar que funciona:

    Terminal window
    .\amper test
    Passed testRoot()
    Test run finished after 573 ms
    [ 1 tests found ]
    [ 1 tests successful ]
    Activitat

    Modifica el missatge que retorna la funció call.respondText del fitxer src/main.kt

    Verifica que el test dona error.

    Modifica el test per tal que accepti la nova implementació.

    Modifica el “path” de l’endpoint GET del fitxer src/main.kt.

    Verifica que el test dona error.

    Modifica el test per tal que accepti la nova implementació.

    Activitats

    Activitat

    Crea el fitxer test/SeqTest.kt.

    Crea les proves necessàries per verificar el funcionament d’aquesta funció:

    import kotlin.test.Test
    import kotlin.test.assertEquals
    fun <T : Comparable<T>> max(list: List<T>): T? {
    return null
    }
    class SeqTest {
    @Test
    fun testMax() {
    assertEquals(10, max(listOf(5, 10, 3)))
    }
    }
    Activitat

    Crea les proves necessàries per verificar el funcionament d’aquesta funció:

    fun <T, R> newList(list: List<T>, f: (T) -> R): List<R> {
    // ...
    }

    Pendent