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!
Modifica la funció shouldFail
per tal que passi la prova.
@Testfun shouldFail() { assertTrue(true)}
Test-Driven Development
Crea el fitxer test/CalculatorTest.kt
amb una prova per a la funció add()
que suma dos números,
import Calculator.addimport kotlin.test.Testimport 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
fun add(a: Int, b: Int) = 0
Ara si executes el test, l’error és diferent, però el programa funciona!! 😺
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ó?
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.
import Calculator.addimport kotlin.test.Testimport kotlin.test.assertEquals@Testfun 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 :10Actual :5
Modifica la funció add
per tal que passi el test, i només aquest test!
fun add(a: Int, b: Int) = if (a == 2) 5 else 10
Encara que una funció passi un conjunt de proves no vol dir que estigui ben implementada!
Assertions
La biblioteca kotlin.test
proporciona un conjunt de funcions que pots utilitzar per validar el comportament del teu codi.
assertEquals | Asserts that the expected value is equal to the actual value |
assertNotEquals | … |
assertTrue | Assert that … |
assertFalse | Assert that … |
assertNull | Assert that … |
assertNotNull | Assert 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ó:
import kotlin.test.assertFailsWith @Testfun 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:
product: jvm/app
# aquestes dependències estan disponibles en el codi principal i de provesdependencies: - io.ktor:ktor-server-core:3.2.0 - io.ktor:ktor-server-netty:3.2.0
# dependències addicionals per al codi de provestest-dependencies: - io.ktor:ktor-server-test-host:3.2.0
A continuació pots implementar un servidor amb un endpoint GET
a la ruta /
:
import io.ktor.server.application.*import io.ktor.server.engine.embeddedServerimport io.ktor.server.netty.Nettyimport 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:
.\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:
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:
.\amper test
Passed testRoot()
Test run finished after 573 ms[ 1 tests found ][ 1 tests successful ]
Modifica el missatge que retorna la funció call.respondText
del fitxer src/main.kt
call.respondText("Hello, David!")
Verifica que el test dona error.
Modifica el test per tal que accepti la nova implementació.
assertEquals("Hello, David!", response.bodyAsText())
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
Crea el fitxer test/SeqTest.kt
.
Crea les proves necessàries per verificar el funcionament d’aquesta funció:
import kotlin.test.Testimport 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)))
}}
Crea les proves necessàries per verificar el funcionament d’aquesta funció:
fun <T, R> newList(list: List<T>, f: (T) -> R): List<R> { // ...}