TL;DR

  • API testing verifica que servicios backend funcionan correctamente sin UI — más rápido y confiable que tests E2E
  • Testea: status codes, cuerpo de respuesta, headers, manejo de errores, autenticación, performance
  • Herramientas: Postman (manual/aprendizaje), REST Assured (Java), Supertest (Node.js), requests (Python)
  • Automatiza en CI/CD — APIs cambian frecuentemente, atrapa breaking changes temprano
  • Cubre happy path y escenarios de error (400s, 401, 404, 500)

Ideal para: Backend developers, ingenieros QA, cualquiera testeando microservicios Omite si: Solo necesitas testear sitios estáticos o frontends simples Tiempo de lectura: 18 minutos

Tus tests de frontend pasan. Los usuarios reportan que la app está rota. La API cambió, y nadie testeó el contrato.

API testing atrapa estos problemas antes de que lleguen a usuarios. Es más rápido que UI testing, más confiable, y testea la lógica de negocio real de la que depende tu aplicación.

Este tutorial enseña API testing desde cero — básicos de HTTP, convenciones REST, autenticación, manejo de errores y automatización con herramientas populares.

¿Qué es API Testing?

API (Application Programming Interface) testing verifica que tus servicios backend funcionan correctamente. En lugar de hacer clic en un UI, envías requests HTTP directamente a endpoints y verificas respuestas.

Qué cubre API testing:

  • Funcionalidad — ¿hace el endpoint lo que debería?
  • Validación de datos — ¿están las respuestas estructuradas correctamente?
  • Manejo de errores — ¿falla gracefully?
  • Autenticación — ¿está el acceso controlado apropiadamente?
  • Performance — ¿puede manejar carga?

Por qué importa API testing:

  • Más rápido que UI tests — sin renderizado de browser, milisegundos vs segundos
  • Más estable — sin selectores flaky o problemas de timing
  • Feedback más temprano — testea antes de que exista el frontend
  • Mejor cobertura — testea edge cases imposibles via UI

Fundamentos HTTP

Antes de testear APIs, entiende los básicos de HTTP.

Métodos HTTP

GET     /users          # Obtener todos los usuarios
GET     /users/123      # Obtener usuario 123
POST    /users          # Crear nuevo usuario
PUT     /users/123      # Reemplazar usuario 123
PATCH   /users/123      # Actualizar partes de usuario 123
DELETE  /users/123      # Eliminar usuario 123
MétodoPropósitoTiene BodyIdempotente
GETLeer datosNo
POSTCrear recursoNo
PUTReemplazar recurso
PATCHActualización parcialNo
DELETEEliminar recursoOpcional

Códigos de Estado

2xx Éxito
├── 200 OK              # Request exitoso
├── 201 Created         # Recurso creado
├── 204 No Content      # Éxito, nada que retornar

4xx Errores de Cliente
├── 400 Bad Request     # Input inválido
├── 401 Unauthorized    # Auth faltante/inválido
├── 403 Forbidden       # Auth válido, sin permiso
├── 404 Not Found       # Recurso no existe
├── 409 Conflict        # Conflicta con estado actual
├── 422 Unprocessable   # Validación falló

5xx Errores de Servidor
├── 500 Internal Error  # Bug del servidor
├── 502 Bad Gateway     # Error upstream
├── 503 Unavailable     # Servidor sobrecargado/mantenimiento

Testing con Postman

Postman es la forma más fácil de empezar con API testing.

Primer Request

  1. Abre Postman
  2. Ingresa URL: https://jsonplaceholder.typicode.com/posts/1
  3. Método: GET
  4. Click en Send

Agregando Tests

En Postman, agrega tests JavaScript en la pestaña “Tests”:

// Verificación de status code
pm.test("Status code is 200", function () {
    pm.response.to.have.status(200);
});

// Tiempo de respuesta
pm.test("Response time is less than 500ms", function () {
    pm.expect(pm.response.responseTime).to.be.below(500);
});

// Validación de body
pm.test("Has correct structure", function () {
    const json = pm.response.json();
    pm.expect(json).to.have.property("id");
    pm.expect(json).to.have.property("title");
    pm.expect(json.id).to.eql(1);
});

// Verificación de header
pm.test("Content-Type is JSON", function () {
    pm.expect(pm.response.headers.get("Content-Type"))
      .to.include("application/json");
});

REST API Testing con Código

Python con requests

import requests
import pytest

BASE_URL = "https://api.example.com"

class TestUsersAPI:

    def test_get_users_returns_list(self):
        response = requests.get(f"{BASE_URL}/users")

        assert response.status_code == 200
        assert isinstance(response.json(), list)

    def test_create_user(self):
        payload = {
            "name": "John Doe",
            "email": "john@example.com"
        }

        response = requests.post(
            f"{BASE_URL}/users",
            json=payload,
            headers={"Content-Type": "application/json"}
        )

        assert response.status_code == 201
        data = response.json()
        assert data["name"] == payload["name"]
        assert "id" in data

    def test_get_nonexistent_user(self):
        response = requests.get(f"{BASE_URL}/users/99999")

        assert response.status_code == 404

    def test_create_user_invalid_email(self):
        payload = {"name": "John", "email": "not-an-email"}

        response = requests.post(f"{BASE_URL}/users", json=payload)

        assert response.status_code == 400

JavaScript con Supertest

const request = require('supertest');
const app = require('../src/app');

describe('Users API', () => {
  test('GET /users returns list of users', async () => {
    const response = await request(app)
      .get('/users')
      .expect(200)
      .expect('Content-Type', /json/);

    expect(Array.isArray(response.body)).toBe(true);
  });

  test('POST /users creates new user', async () => {
    const newUser = {
      name: 'John Doe',
      email: 'john@example.com'
    };

    const response = await request(app)
      .post('/users')
      .send(newUser)
      .expect(201);

    expect(response.body).toMatchObject(newUser);
    expect(response.body.id).toBeDefined();
  });
});

Java con REST Assured

import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.*;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

public class UsersApiTest {

    @BeforeAll
    public static void setup() {
        RestAssured.baseURI = "https://api.example.com";
    }

    @Test
    public void getUsersReturnsList() {
        given()
            .when()
                .get("/users")
            .then()
                .statusCode(200)
                .contentType(ContentType.JSON);
    }

    @Test
    public void createUserReturnsCreated() {
        String requestBody = """
            {
                "name": "John Doe",
                "email": "john@example.com"
            }
            """;

        given()
            .contentType(ContentType.JSON)
            .body(requestBody)
        .when()
            .post("/users")
        .then()
            .statusCode(201)
            .body("name", equalTo("John Doe"))
            .body("id", notNullValue());
    }
}

Testing de Autenticación

Bearer Token (JWT)

# Paso 1: Login para obtener token
login_response = requests.post(
    "https://api.example.com/auth/login",
    json={"email": "user@example.com", "password": "secret"}
)
token = login_response.json()["token"]

# Paso 2: Usar token en requests
response = requests.get(
    "https://api.example.com/protected",
    headers={"Authorization": f"Bearer {token}"}
)

Testing de Escenarios de Auth

class TestAuthentication:

    def test_protected_endpoint_requires_auth(self):
        response = requests.get(f"{BASE_URL}/protected")
        assert response.status_code == 401

    def test_invalid_token_rejected(self):
        response = requests.get(
            f"{BASE_URL}/protected",
            headers={"Authorization": "Bearer invalid_token"}
        )
        assert response.status_code == 401

    def test_valid_token_grants_access(self):
        token = get_valid_token()
        response = requests.get(
            f"{BASE_URL}/protected",
            headers={"Authorization": f"Bearer {token}"}
        )
        assert response.status_code == 200

Testing GraphQL

GraphQL usa un único endpoint con queries y mutations.

Testing de Query

def test_graphql_query():
    query = """
    query GetUser($id: ID!) {
        user(id: $id) {
            id
            name
            email
        }
    }
    """

    response = requests.post(
        f"{BASE_URL}/graphql",
        json={
            "query": query,
            "variables": {"id": "123"}
        }
    )

    assert response.status_code == 200
    data = response.json()
    assert "errors" not in data
    assert data["data"]["user"]["id"] == "123"

Testing de Errores

Testea cómo tu API maneja problemas.

class TestErrorHandling:

    def test_malformed_json_returns_400(self):
        response = requests.post(
            f"{BASE_URL}/users",
            data="not valid json",
            headers={"Content-Type": "application/json"}
        )
        assert response.status_code == 400

    def test_missing_required_field_returns_400(self):
        response = requests.post(
            f"{BASE_URL}/users",
            json={"name": "John"}  # falta email
        )
        assert response.status_code == 400

    def test_duplicate_email_returns_409(self):
        # Crear primer usuario
        requests.post(f"{BASE_URL}/users", json={
            "name": "John", "email": "john@example.com"
        })

        # Intentar crear duplicado
        response = requests.post(f"{BASE_URL}/users", json={
            "name": "Jane", "email": "john@example.com"
        })

        assert response.status_code == 409

Performance Testing

Load Testing con k6

// k6 script: load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 20 },   // Ramp a 20 usuarios
    { duration: '1m', target: 20 },    // Mantener 20 usuarios
    { duration: '10s', target: 0 },    // Ramp down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% bajo 500ms
    http_req_failed: ['rate<0.01'],    // Menos de 1% fallos
  },
};

export default function () {
  const response = http.get('https://api.example.com/users');

  check(response, {
    'status is 200': (r) => r.status === 200,
    'response time OK': (r) => r.timings.duration < 500,
  });

  sleep(1);
}

Testing API con Asistencia de IA

Las herramientas de IA pueden acelerar el desarrollo de tests de API.

Lo que la IA hace bien:

  • Generar casos de test desde specs OpenAPI/Swagger
  • Crear datos de test válidos e inválidos
  • Escribir boilerplate para patrones comunes
  • Sugerir edge cases para testear

Lo que aún necesita humanos:

  • Entender requisitos de negocio
  • Diseñar estrategia de testing
  • Debuggear tests flaky
  • Interpretar resultados de performance

FAQ

¿Qué es el testing de API?

API testing verifica que las APIs funcionan correctamente enviando requests HTTP y validando respuestas. Testea funcionalidad, validación de datos, manejo de errores, autenticación y performance. A diferencia de UI testing, API testing prueba directamente la capa de lógica de negocio.

¿Qué herramientas se usan para API testing?

Herramientas populares incluyen:

  • Postman — herramienta GUI para testing manual y automatización
  • REST Assured — librería Java para API testing
  • Supertest — Node.js/JavaScript API testing
  • requests + pytest — Python API testing
  • k6 — Performance y load testing

¿Cuál es la diferencia entre API testing y unit testing?

Unit tests verifican funciones individuales aisladas, mockeando todas las dependencias. API tests verifican endpoints HTTP completos, incluyendo routing, middleware, autenticación, operaciones de BD y formateo de respuestas. API tests son tests de integración que verifican que los componentes funcionan juntos.

¿Cómo testeo APIs autenticadas?

  1. Envía request de login con credenciales
  2. Extrae token de la respuesta
  3. Incluye token en header Authorization para requests siguientes
  4. Guarda token en variable de entorno para reusar
  5. Implementa refresh de token para tokens que expiran

Recursos Oficiales

Ver También