Que Es REST Assured?

REST Assured es una biblioteca Java que simplifica el testing de APIs RESTful. Proporciona un lenguaje de dominio especifico (DSL) construido sobre un patron Given-When-Then que hace que los tests de API se lean como especificaciones en lenguaje natural. En lugar de construir manualmente peticiones HTTP, parsear respuestas y escribir aserciones complejas, REST Assured maneja estas operaciones con una API fluida y encadenable.

REST Assured es la biblioteca de testing de API mas popular en el ecosistema Java, usada por miles de organizaciones para validacion automatizada de APIs. Se integra perfectamente con JUnit, TestNG, Maven, Gradle y pipelines de CI/CD.

Configuracion del Proyecto

Configuracion Maven

<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>5.4.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>json-schema-validator</artifactId>
        <version>5.4.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

El Patron Given-When-Then

Cada test de REST Assured sigue tres fases:

given()    // Configurar la peticion: headers, body, params, auth
  .header("Content-Type", "application/json")
  .body(requestBody)
.when()    // Ejecutar el metodo HTTP
  .post("/api/users")
.then()    // Validar la respuesta
  .statusCode(201)
  .body("name", equalTo("Alice"));

Operaciones CRUD

Peticion GET

@Test
void shouldReturnAllUsers() {
    given()
        .baseUri("https://api.example.com")
    .when()
        .get("/api/users")
    .then()
        .statusCode(200)
        .body("size()", greaterThan(0))
        .body("[0].name", notNullValue());
}

@Test
void shouldReturnUserById() {
    given()
        .baseUri("https://api.example.com")
        .pathParam("id", 42)
    .when()
        .get("/api/users/{id}")
    .then()
        .statusCode(200)
        .body("id", equalTo(42))
        .body("name", equalTo("Alice"));
}

Peticion POST

@Test
void shouldCreateNewUser() {
    String requestBody = """
        {
            "name": "Bob Johnson",
            "email": "bob@example.com",
            "role": "developer"
        }
        """;

    given()
        .baseUri("https://api.example.com")
        .contentType(ContentType.JSON)
        .body(requestBody)
    .when()
        .post("/api/users")
    .then()
        .statusCode(201)
        .body("id", notNullValue())
        .body("name", equalTo("Bob Johnson"));
}

PUT y DELETE

@Test
void shouldUpdateUser() {
    String updateBody = """
        { "name": "Alice Updated", "role": "lead" }
        """;

    given()
        .contentType(ContentType.JSON)
        .body(updateBody)
    .when()
        .put("/api/users/42")
    .then()
        .statusCode(200)
        .body("name", equalTo("Alice Updated"));
}

@Test
void shouldDeleteUser() {
    when()
        .delete("/api/users/42")
    .then()
        .statusCode(204);
}

Parametros de Consulta y Headers

@Test
void shouldFilterUsersWithQueryParams() {
    given()
        .queryParam("role", "admin")
        .queryParam("active", true)
        .queryParam("page", 1)
    .when()
        .get("/api/users")
    .then()
        .statusCode(200)
        .body("every { it.role == 'admin' }", is(true));
}

Extraccion de Respuesta

@Test
void shouldExtractAndUseResponseData() {
    int userId = given()
        .contentType(ContentType.JSON)
        .body("{ \"name\": \"Test User\", \"email\": \"test@example.com\" }")
    .when()
        .post("/api/users")
    .then()
        .statusCode(201)
        .extract()
        .path("id");

    given()
        .pathParam("id", userId)
    .when()
        .get("/api/users/{id}")
    .then()
        .statusCode(200)
        .body("name", equalTo("Test User"));
}

Autenticacion

// Basic Auth
given()
    .auth().basic("username", "password")
.when()
    .get("/api/protected");

// Bearer Token
given()
    .header("Authorization", "Bearer " + token)
.when()
    .get("/api/protected");

// OAuth 2.0
given()
    .auth().oauth2(accessToken)
.when()
    .get("/api/protected");

Validacion de Esquema JSON

@Test
void shouldMatchUserSchema() {
    given()
    .when()
        .get("/api/users/1")
    .then()
        .statusCode(200)
        .body(matchesJsonSchemaInClasspath("schemas/user-schema.json"));
}

Especificaciones Reutilizables

public class ApiSpecs {
    public static RequestSpecification baseSpec() {
        return new RequestSpecBuilder()
            .setBaseUri("https://api.example.com")
            .setContentType(ContentType.JSON)
            .addHeader("X-API-Version", "2")
            .build();
    }

    public static RequestSpecification authSpec(String token) {
        return new RequestSpecBuilder()
            .addRequestSpecification(baseSpec())
            .addHeader("Authorization", "Bearer " + token)
            .build();
    }
}

// Uso en tests
@Test
void shouldGetUserWithSpecs() {
    given()
        .spec(ApiSpecs.authSpec(token))
    .when()
        .get("/api/users/1")
    .then()
        .statusCode(200)
        .body("name", notNullValue());
}

Logging para Depuracion

given()
    .log().all()          // Log de peticion completa
.when()
    .get("/api/users")
.then()
    .log().ifError()      // Log de respuesta solo en error
    .statusCode(200);

Ejercicios

Ejercicio 1: Suite de Tests CRUD

Construye una suite de tests CRUD completa para una REST API:

  1. POST — Crear un nuevo recurso, extraer su ID
  2. GET — Recuperar el recurso por ID, validar todos los campos
  3. PUT — Actualizar campos especificos, verificar cambios
  4. DELETE — Eliminar el recurso, verificar 404 en GET posterior
  5. Usar RequestSpecification reutilizable para base URL y auth

Ejercicio 2: Validacion de Esquema

  1. Crea un archivo de esquema JSON para un objeto de respuesta de usuario
  2. Escribe un test que valide el esquema para endpoints de usuario individual y lista
  3. Modifica el esquema para hacer un campo obligatorio y verifica que el test detecte una violacion
  4. Agrega validacion de esquema a tu suite CRUD del Ejercicio 1

Ejercicio 3: Flujo de Autenticacion

  1. Escribe un test que autentique via POST /auth/login y extraiga un token JWT
  2. Usa el token extraido para peticiones autenticadas posteriores
  3. Testea que las peticiones no autenticadas devuelvan 401
  4. Testea que los tokens expirados devuelvan 403 con un mensaje de error apropiado