¿Qué es Gatling?
Gatling es una herramienta de pruebas de carga open-source y de alto rendimiento diseñada para integración continua y flujos de trabajo orientados a desarrolladores. Construido sobre Scala con una arquitectura asíncrona no-bloqueante (Akka + Netty), Gatling puede simular miles de usuarios concurrentes con un consumo de memoria significativamente menor que herramientas basadas en hilos como JMeter.
Gatling produce reportes HTML detallados e interactivos de forma nativa — a menudo considerados los reportes de performance testing más atractivos de la industria. Estos reportes incluyen distribuciones de tiempo de respuesta, percentiles, conteo de solicitudes en el tiempo y análisis de errores.
Cuándo Elegir Gatling
Cada herramienta de pruebas de carga tiene sus fortalezas. Así se compara Gatling.
| Característica | Gatling | JMeter | k6 |
|---|---|---|---|
| Lenguaje | DSL Scala/Java/Kotlin | GUI + XML | JavaScript |
| Arquitectura | Async no-bloqueante | Un hilo por usuario | Goroutines de Go |
| Reportes | HTML hermosos integrados | Básicos, necesita plugins | Terminal + JSON/CSV |
| CI/CD | Plugin Maven/Gradle/sbt | Modo CLI | CLI nativo |
| Protocolo | HTTP, WebSocket, JMS, MQTT | HTTP, JDBC, FTP, LDAP, JMS | HTTP, WebSocket, gRPC |
| Uso de recursos | Muy bajo | Alto | Bajo |
| Curva de aprendizaje | Pronunciada (Scala) | Moderada | Fácil (JS) |
Elige Gatling cuando: Necesitas reportes excelentes, tu equipo usa lenguajes JVM, quieres bajo consumo de recursos a escala, o necesitas integración nativa con Maven/Gradle para CI/CD.
Estructura de Simulación en Gatling
Una simulación de Gatling es una clase Scala que extiende Simulation. Contiene tres partes principales:
- Configuración de protocolo — Configuración HTTP, URL base, headers
- Definición de escenario — El recorrido del usuario (secuencia de solicitudes)
- Perfil de inyección — Cómo se agregan usuarios en el tiempo
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class BasicSimulation extends Simulation {
// 1. Configuración de protocolo
val httpProtocol = http
.baseUrl("https://api.example.com")
.acceptHeader("application/json")
.contentTypeHeader("application/json")
// 2. Definición de escenario
val scn = scenario("Explorar Productos")
.exec(
http("Listar Productos")
.get("/api/products")
.check(status.is(200))
.check(jsonPath("$.products").exists)
)
.pause(2)
.exec(
http("Detalles del Producto")
.get("/api/products/1")
.check(status.is(200))
.check(jsonPath("$.name").saveAs("productName"))
)
.pause(1)
// 3. Setup con perfil de inyección
setUp(
scn.inject(
rampUsers(100).during(60.seconds)
)
).protocols(httpProtocol)
.assertions(
global.responseTime.percentile3.lt(1000),
global.successfulRequests.percent.gt(99)
)
}
Configuración de Protocolo
El builder del protocolo HTTP define configuraciones compartidas para todas las solicitudes:
val httpProtocol = http
.baseUrl("https://api.example.com")
.acceptHeader("application/json")
.acceptEncodingHeader("gzip, deflate")
.userAgentHeader("Gatling/3.9")
.shareConnections
Definición de Escenario
Un escenario es una secuencia de acciones que un usuario virtual realiza:
val scn = scenario("Recorrido del Usuario")
.exec(http("Página Principal").get("/"))
.pause(1, 3) // pausa aleatoria entre 1-3 segundos
.exec(http("Login").post("/login")
.body(StringBody("""{"user":"test","pass":"123"}"""))
.check(jsonPath("$.token").saveAs("authToken"))
)
.exec(http("Dashboard").get("/dashboard")
.header("Authorization", "Bearer ${authToken}")
)
Acciones clave:
exec()— Ejecutar una solicitud HTTPpause()— Esperar entre solicitudes (think time)repeat()— Repetir una secuencia N vecesduring()— Repetir durante una duración especificadadoIf()/doIfOrElse()— Ejecución condicionalfeed()— Inyectar datos desde un feeder (CSV, JSON, JDBC)
Feeders (Parametrización de Datos)
Los feeders proporcionan datos de prueba a los usuarios virtuales:
// Feeder CSV
val csvFeeder = csv("users.csv").circular // reciclar al agotarse
// Feeder JSON
val jsonFeeder = jsonFile("products.json").random
// Feeder en línea
val inlineFeeder = Iterator.continually(Map(
"username" -> s"user_${scala.util.Random.nextInt(1000)}",
"email" -> s"user${scala.util.Random.nextInt(1000)}@test.com"
))
val scn = scenario("Test Parametrizado")
.feed(csvFeeder)
.exec(http("Login")
.post("/login")
.body(StringBody("""{"username":"${username}","password":"${password}"}"""))
)
Checks y Assertions
Checks validan respuestas individuales:
http("Obtener Usuario")
.get("/api/users/1")
.check(status.is(200))
.check(jsonPath("$.name").is("John"))
.check(responseTimeInMillis.lt(500))
.check(jsonPath("$.id").saveAs("userId"))
.check(header("Content-Type").is("application/json"))
Assertions definen criterios de aprobación/rechazo para toda la simulación:
setUp(scn.inject(...))
.assertions(
global.responseTime.max.lt(5000),
global.responseTime.percentile3.lt(1000),
global.successfulRequests.percent.gt(99),
details("Login").responseTime.mean.lt(500)
)
Perfiles de Inyección
Los perfiles de inyección definen cómo se introducen usuarios virtuales en la simulación. Gatling ofrece varias estrategias:
setUp(
scn.inject(
nothingFor(5.seconds),
atOnceUsers(10),
rampUsers(100).during(60.seconds),
constantUsersPerSec(20).during(120.seconds),
rampUsersPerSec(1).to(50).during(60.seconds)
)
)
Estos perfiles se pueden combinar para crear patrones de carga complejos.
Ejecutar Gatling
# Usando el bundle
./bin/gatling.sh
# Usando Maven
mvn gatling:test
# Usando Gradle
gradle gatlingRun
# Usando sbt
sbt Gatling/test
Gatling genera un reporte HTML en target/gatling/ con gráficos de tiempos de respuesta, throughput y usuarios activos en el tiempo.
Ejercicio: Diseñar una Simulación en Gatling
Crea una simulación de Gatling para una plataforma de subastas en línea.
Escenario
La plataforma de subastas tiene estos endpoints:
| Endpoint | Método | Descripción |
|---|---|---|
/api/auth/login | POST | Login de usuario |
/api/auctions | GET | Listar subastas activas |
/api/auctions/{id} | GET | Detalles de subasta |
/api/auctions/{id}/bid | POST | Realizar una oferta |
Requisitos
- Usa un feeder CSV para credenciales de usuario
- Encadena solicitudes: login, explorar subastas, ver detalles, hacer oferta
- Extrae el token de auth del login y el ID de subasta de la lista
- Agrega pausas entre acciones (1-3 segundos)
- Perfil de inyección: 50 usuarios en ramp-up de 2 minutos, mantener 3 minutos, bajar
- Assertions: p95 < 1000ms, tasa de éxito > 98%
Pista: Estructura de la Simulación
class AuctionSimulation extends Simulation {
val httpProtocol = http.baseUrl("https://auction-api.example.com")
val csvFeeder = csv("users.csv").circular
val scn = scenario("Ofertas en Subasta")
.feed(csvFeeder)
.exec(/* login */)
.pause(1, 3)
.exec(/* listar subastas, guardar auctionId */)
.pause(1, 2)
.exec(/* ver detalles de subasta */)
.pause(1, 2)
.exec(/* hacer oferta */)
setUp(
scn.inject(
rampUsers(50).during(120.seconds),
nothingFor(180.seconds)
)
).protocols(httpProtocol)
.assertions(/* tus assertions */)
}
Solución: Simulación Completa de Gatling
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class AuctionSimulation extends Simulation {
val httpProtocol = http
.baseUrl("https://auction-api.example.com")
.acceptHeader("application/json")
.contentTypeHeader("application/json")
val csvFeeder = csv("users.csv").circular
val scn = scenario("Flujo de Ofertas en Subasta")
.feed(csvFeeder)
.exec(
http("Login")
.post("/api/auth/login")
.body(StringBody("""{"username":"${username}","password":"${password}"}"""))
.check(status.is(200))
.check(jsonPath("$.token").saveAs("authToken"))
)
.pause(1, 3)
.exec(
http("Listar Subastas")
.get("/api/auctions")
.header("Authorization", "Bearer ${authToken}")
.check(status.is(200))
.check(jsonPath("$.auctions[0].id").saveAs("auctionId"))
.check(jsonPath("$.auctions[0].currentBid").saveAs("currentBid"))
)
.pause(1, 2)
.exec(
http("Detalles de Subasta")
.get("/api/auctions/${auctionId}")
.header("Authorization", "Bearer ${authToken}")
.check(status.is(200))
.check(jsonPath("$.title").exists)
)
.pause(1, 2)
.exec(session => {
val currentBid = session("currentBid").as[String].toDouble
val newBid = currentBid + 10.0
session.set("newBid", newBid.toString)
})
.exec(
http("Hacer Oferta")
.post("/api/auctions/${auctionId}/bid")
.header("Authorization", "Bearer ${authToken}")
.body(StringBody("""{"amount":${newBid}}"""))
.check(status.in(200, 201))
)
setUp(
scn.inject(
rampUsers(50).during(120.seconds),
nothingFor(180.seconds)
)
).protocols(httpProtocol)
.assertions(
global.responseTime.percentile(95).lt(1000),
global.successfulRequests.percent.gt(98)
)
}
Análisis del Reporte HTML:
- Distribución de Tiempo de Respuesta: Verifica que el histograma se incline a la izquierda (respuestas rápidas)
- Usuarios Activos en el Tiempo: Verifica que el patrón de ramp-up coincida con tu perfil de inyección
- Solicitudes por Segundo: Busca estabilidad en throughput durante el estado estable
- Percentiles de Tiempo de Respuesta: p95 debe mantenerse bajo 1000ms durante toda la prueba
- Pestaña de Errores: Identifica qué solicitudes fallan y con cuántos usuarios
Tips Profesionales
- DSL Java/Kotlin: Si tu equipo no conoce Scala, Gatling 3.7+ soporta DSLs en Java y Kotlin igualmente potentes. El DSL de Java es el punto de partida recomendado para la mayoría de los equipos.
- Modelo Cerrado vs Abierto: Gatling soporta tanto modelo cerrado (número fijo de usuarios concurrentes) como modelo abierto (usuarios llegan a una tasa). Usa modelo abierto para aplicaciones web donde constantemente llegan nuevos usuarios independientemente de cuántos están activos.
- Gatling Recorder: Similar al grabador de JMeter, Gatling incluye un convertidor HAR y grabador de proxy HTTP para capturar tráfico del navegador y convertirlo en scripts de Gatling.
- Maven Archetype: Inicia nuevos proyectos con
mvn archetype:generate -DarchetypeGroupId=io.gatling.highcharts -DarchetypeArtifactId=gatling-highcharts-maven-archetypepara una estructura de proyecto preconfigurada. - Integración CI: Los plugins Maven/Gradle de Gatling facilitan ejecutar pruebas de carga en Jenkins, GitHub Actions o GitLab CI. Configura assertions para fallar el build si el rendimiento se degrada.