sábado, 18 de octubre de 2025

Consumiendo una API pública con Kotlin & OkHttp

En el pasado post vimos cómo consumir un servicio Node desde una aplicación Java gracias a la librería OkHttp. Esta vez usaremos Kotlin como lenguaje base y Gradle para la administración de las dependencias y ejecución del proyecto.

¿Qué haremos?

1. Comenzaremos con la creación del directorio del proyecto:

$ mkdir proyecto-kotlin
$ cd proyecto-kotlin

El directorio del proyecto se verá así:

proyecto-kotlin/
   .gitattributes
   .gitignore
   .gradle
   .kotlin
   app/
   build/
   gradle/
   gradle.properties
   gradlew
   gradlew.bat
   settings.gradle.kts

2. Agregamos la dependencia de ``OkHttp`` en el archivo ``build.gradle.kts``:

plugins {
    alias(libs.plugins.kotlin.jvm)
    application
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.12.0") // Moved to implementation
    testImplementation("org.jetbrains.kotlin:kotlin-test")
    testImplementation(libs.junit.jupiter.engine)
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    implementation(libs.guava)
    testImplementation("org.junit.jupiter:junit-jupiter:5.10.3")
    testImplementation("org.mockito:mockito-core:5.12.0")
    testImplementation("org.mockito:mockito-junit-jupiter:5.12.0")
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

application {
    mainClass = "org.inforhomex.AppKt"
}

tasks.named<Test>("test") {
    useJUnitPlatform()
}

Con esto se podrá descargar la dependencia.

3. Editamos el programa ``app/src/main/kotlin/org/inforhomex/App.kt``.

package org.inforhomex

import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.IOException

private const val URL_API = "https://api.chucknorris.io/jokes/random"

fun run(client: OkHttpClient = OkHttpClient()) {
    val request = Request.Builder().url(URL_API).build()
    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw IOException("Unexpected code $response")
        for ((name: String, value: String) in response.headers) {
            println("$name: $value")
        }
        println(response.body?.string() ?: "No response body")
    }
}

fun main() {
    run()
}

4. Creamos una prueba unitaria ``AppTest.kt``

package org.inforhomex


import okhttp3.*
import okio.Buffer
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.jupiter.MockitoExtension
import java.io.IOException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

@ExtendWith(MockitoExtension::class)
class AppTest {

    @Mock
    private lateinit var client: OkHttpClient

    @Mock
    private lateinit var response: Response

    @Mock
    private lateinit var responseBody: ResponseBody

    @Mock
    private lateinit var call: Call

    private val urlApi = "https://api.chucknorris.io/jokes/random"

    @BeforeEach
    fun setUp() {
        
        Mockito.`when`(client.newCall(Mockito.any(Request::class.java))).thenReturn(call)
    }

    @Test
    fun `test run success response`() {
        
        val mockJson = """
            {
                "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
                "id": "abc123",
                "url": "",
                "value": "Chuck Norris can divide by zero."
            }
        """
        Mockito.`when`(call.execute()).thenReturn(response)
        Mockito.`when`(response.isSuccessful).thenReturn(true)
        Mockito.`when`(response.body).thenReturn(responseBody)
        Mockito.`when`(responseBody.string()).thenReturn(mockJson)
        Mockito.`when`(response.headers).thenReturn(Headers.Builder().add("Content-Type", "application/json").build())

        
        val output = captureOutput {
            run(client) 
        }

        
        assert(output.contains("Content-Type: application/json"))
        assert(output.contains("Chuck Norris can divide by zero."))
    }

    @Test
    fun `test run unsuccessful response throws IOException`() {
       
        Mockito.`when`(call.execute()).thenReturn(response)
        Mockito.`when`(response.isSuccessful).thenReturn(false)
        Mockito.`when`(response.code).thenReturn(404)

       
        val exception = assertFailsWith<IOException> {
            run(client) 
        }
        assertEquals("Unexpected code 404", exception.message)
    }

    
    private fun captureOutput(block: () -> Unit): String {
        val outputStream = java.io.ByteArrayOutputStream()
        val printStream = java.io.PrintStream(outputStream)
        val originalOut = System.out
        System.setOut(printStream)
        try {
            block()
        } finally {
            System.setOut(originalOut)
        }
        return outputStream.toString()
    }
}

5. Compilamos y ejecutamos la aplicación:

$ gradle build
$ gradle run

Si todo va bien, veremos esto en la consola:

date: Sun, 19 Oct 2025 03:25:17 GMT
content-type: application/json
nel: {"report_to":"heroku-nel","response_headers":["Via"],"max_age":3600,"success_fraction":0.01,"failure_fraction":0.1}
report-to: {"group":"heroku-nel","endpoints":[{"url":"https://nel.heroku.com/reports?s=e%2BRTonTfjrgED0WXfh1o2mxbl6%2BLhQQ4LIu%2F1NhF4MM%3D\u0026sid=812dcc77-0bd0-43b1-a5f1-b25750382959\u0026ts=1760844317"}],"max_age":3600}
reporting-endpoints: heroku-nel="https://nel.heroku.com/reports?s=e%2BRTonTfjrgED0WXfh1o2mxbl6%2BLhQQ4LIu%2F1NhF4MM%3D&sid=812dcc77-0bd0-43b1-a5f1-b25750382959&ts=1760844317"
server: cloudflare
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
via: 1.1 heroku-router
cf-cache-status: DYNAMIC
cf-ray: 990d2ad69c2f69de-DFW
alt-svc: h3=":443"; ma=86400
{"categories":[],"created_at":"2020-01-05 13:42:20.262289","icon_url":"https://api.chucknorris.io/img/avatar/chuck-norris.png","id":"zDUih4UaS82sDRjSX0hlLQ","updated_at":"2020-01-05 13:42:20.262289","url":"https://api.chucknorris.io/jokes/zDUih4UaS82sDRjSX0hlLQ","value":"Chuck Norris always wanted to surf but couldn't. Everytime his board touches the water, the sea parts."}

¡La aplicación ha funcionado correctamente!

Continuaremos con esta serie de ejemplos en próximas entregas.

Enlaces:

https://square.github.io/okhttp/
https://www.baeldung.com/guide-to-okhttp
https://ironpdf.com/es/java/blog/java-help/okhttp-java/


No hay comentarios:

Publicar un comentario

Consumiendo una API pública con Kotlin & OkHttp

En el pasado post  vimos cómo consumir un servicio Node desde una aplicación Java gracias a la librería OkHttp . Esta vez usaremos Kotlin...

Etiquetas

Archivo del blog