Cómo probar la API de Rust

INEZA Felin-Michel

INEZA Felin-Michel

13 May 2026

Cómo probar la API de Rust

Apidog para empresas

Despliegue local

SSO & RBAC

Conforme con SOC 2

Explorar Apidog Enterprise

Rust te proporciona un servidor HTTP rápido y con seguridad de tipos en unas pocas cientos de líneas. Lo que no te da es un ciclo de retroalimentación rápido para probar ese servidor. El ciclo de compilación es largo, cargo test vuelve a ejecutar todo ante un solo cambio de trait, y la mayoría de los frameworks HTTP de Rust te obligan a escribir una prueba de integración separada para cada endpoint antes incluso de haberlo llamado una vez. Si quieres lanzar una API y no solo un binario, necesitas una herramienta que viva fuera de la cadena de herramientas de Rust y se comunique con el servidor en ejecución.

Esta guía recorre el flujo de trabajo completo de prueba de API de Rust dentro de Apidog: apuntar Apidog a tu servidor Axum o Actix, construir solicitudes contra tus endpoints, validar JSON serializado con Serde, manejar autenticación JWT, simular endpoints para que el frontend pueda avanzar mientras terminas el handler, y empaquetar todo como un escenario de prueba de CI. Al final, tendrás un proyecto Apidog reutilizable que detecta desviaciones de contrato antes de que cargo build --release termine.

Si vienes de un flujo de trabajo con Postman o curl, también obtienes las funciones de diseño-primero de Apidog de forma gratuita: una especificación OpenAPI generada a partir de tus solicitudes guardadas, URLs de simulación compartibles y entornos de equipo. Deja la historia de migración de Postman para una lectura aparte; esta publicación se centra en Rust.

TL;DR

Por qué probar una API de Rust fuera de la cadena de herramientas de Rust

cargo test es bueno. También es lento, opaco para compañeros de equipo que no usan Rust, y está construido alrededor de código en lugar de HTTP. Si quieres verificar que tu handler devuelve el código de estado correcto, la forma JSON correcta, los encabezados correctos y el mensaje de error adecuado cuando la entrada está mal formada, escribes una nueva llamada tower::ServiceExt::oneshot para cada caso. Luego mantienes esa prueba a medida que el handler cambia. Luego la escribes de nuevo en JavaScript para que el frontend pueda usar una simulación.

Apidog te proporciona una única capa de contrato sobre el servidor en ejecución. La solicitud vive una vez. Las aserciones viven junto a la solicitud. Los compañeros de equipo de frontend abren el mismo proyecto y ven las mismas solicitudes que tú. Cuando Serde obtenga un atributo #[serde(rename_all = "camelCase")] dentro de tres semanas, la prueba que se romperá será la de Apidog, no la que se envía a producción.

Tres razones concretas para añadir Apidog a un flujo de trabajo de Rust:

  1. Las verificaciones de contrato se desacoplan de la compilación. Apidog se ejecuta contra un binario en ejecución. Dejas de esperar a rustc para validar que tu endpoint sigue devolviendo 200.
  2. Las simulaciones son compartibles. Un desarrollador frontend en otra zona horaria obtiene una URL que devuelve el JSON correcto, no un mensaje de Slack que dice "el handler aún no está listo".
  3. OpenAPI gratis. Apidog puede generar un documento OpenAPI 3.1 a partir de las solicitudes guardadas. Se lo entregas a cualquiera que quiera un cliente tipado sin escribir una anotación utoipa o aide en cada ruta.

Paso 1: Añade tu servidor Rust como un entorno de Apidog

Inicia tu API de Rust. Para un proyecto Axum, el código boilerplate es:

use axum::{routing::get, Router};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/healthz", get(|| async { "ok" }));
    let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Abre Apidog, crea un nuevo proyecto, luego abre Gestión de Entornos (menú desplegable superior derecho) y añade un entorno llamado Rust Local:

Variable Valor
baseUrl http://localhost:3000
token déjalo vacío por ahora
apiVersion v1

Añade un segundo entorno llamado Rust Staging con la URL base desplegada. Apidog limita las variables por entorno, por lo que cambias de local a staging con un solo clic en el menú desplegable. Sin buscar y reemplazar en las solicitudes guardadas.

Paso 2: Haz una solicitud al primer endpoint

Crea una carpeta llamada Rust API dentro del proyecto, luego una nueva solicitud:

Haz clic en Enviar. Si tu servidor está en ejecución, obtendrás un 200 con el cuerpo ok. Guarda esto como health-check. Es la prueba de humo más sencilla posible, y confirma que el entorno y la URL base funcionan antes de que escribas algo más interesante.

Si obtienes un error de conexión rechazada, tu servidor no está enlazado a 0.0.0.0 o el puerto es incorrecto. El TcpListener::bind("127.0.0.1:3000") predeterminado de Rust rechazará las solicitudes que provengan de cualquier cosa que se resuelva como localhost en una interfaz diferente; enlaza a 0.0.0.0 para el desarrollo local para que Apidog y los contenedores Docker puedan alcanzarlo.

Paso 3: Prueba solicitudes y respuestas JSON con Serde

La forma más común de API de Rust es un handler de JSON de entrada y JSON de salida respaldado por un struct de Serde. Añade una ruta POST /users:

use axum::{extract::Json, routing::post, Router};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

async fn create_user(Json(payload): Json<CreateUser>) -> Json<User> {
    Json(User { id: 1, name: payload.name, email: payload.email })
}

let app = Router::new().route("/users", post(create_user));

En Apidog, crea una solicitud:

{
  "name": "Ada Lovelace",
  "email": "ada@example.com"
}

Envíala. Recibirás el JSON de User. Guárdala como create-user.

Ahora abre la pestaña Pruebas y añade aserciones:

pm.test("Status is 200", () => {
  pm.expect(pm.response.code).to.eql(200);
});

pm.test("Body has id, name, email", () => {
  const body = pm.response.json();
  pm.expect(body).to.have.property("id");
  pm.expect(body.name).to.eql("Ada Lovelace");
  pm.expect(body.email).to.match(/^[^@]+@[^@]+$/);
});

La próxima vez que alguien añada #[serde(rename_all = "camelCase")] al struct y la forma de tu respuesta cambie de user_id a userId, esta prueba fallará antes de que el cambio se implemente. Ese es el contrato que Apidog te ofrece y cargo test no, porque cargo test ejecuta tu código Rust contra tus tipos Rust y pasaría felizmente con cualquiera de las dos formas.

Paso 4: Cubre los casos de rechazo de Serde

La parte interesante del manejo de JSON en Rust es lo que Serde hace con una entrada incorrecta. Por defecto, Axum devuelve un 422 Entidad No Procesable sin detalles. Construye tres solicitudes que rompan intencionadamente el esquema:

Solicitud Cuerpo Esperado
create-user-missing-email { "name": "Ada" } 422, el cuerpo menciona missing field email
create-user-extra-field { "name": "Ada", "email": "a@b.c", "admin": true } 200 si #[serde(deny_unknown_fields)] está ausente; 422 en caso contrario
create-user-wrong-type { "name": 1, "email": "a@b.c" } 422, menciona invalid type: integer

Afirma cada código de estado en Pruebas. Esta es la forma más económica de documentar tu política de validación real. Si activas deny_unknown_fields más tarde, la segunda prueba se pondrá en rojo y te indicará que el contrato público ha cambiado.

Paso 5: Probar rutas protegidas con JWT

La mayoría de las APIs de Rust en producción ocultan los handlers detrás de un middleware de autenticación. El extractor JWT axum-extra de Axum es el patrón común:

use axum_extra::extract::cookie::PrivateCookieJar;
use jsonwebtoken::{decode, DecodingKey, Validation};

async fn me(jar: PrivateCookieJar) -> Result<Json<User>, StatusCode> {
    let token = jar.get("token").ok_or(StatusCode::UNAUTHORIZED)?;
    let claims = decode::<Claims>(token.value(), &DecodingKey::from_secret(b"secret"), &Validation::default())
        .map_err(|_| StatusCode::UNAUTHORIZED)?;
    Ok(Json(User { id: claims.claims.sub, name: "Ada".into(), email: "ada@example.com".into() }))
}

En Apidog, no necesitas crear un JWT manualmente en cada ejecución de prueba. Crea un Script Pre-Solicitud en la carpeta:

const jwt = require("jsonwebtoken");
const token = jwt.sign(
  { sub: 1, exp: Math.floor(Date.now() / 1000) + 3600 },
  "secret"
);
pm.environment.set("token", token);

Abre la configuración de la carpeta, establece Auth en Token Bearer, valor {{token}}. Cada solicitud en la carpeta ahora firma y presenta un JWT nuevo. Los errores de tokens caducados desaparecen de tus ejecuciones de prueba. Para una explicación más detallada sobre las aserciones, consulta cómo probar la autenticación JWT en APIs.

Paso 6: Probar streaming y eventos enviados por el servidor (Server-Sent Events)

Los frameworks web de Rust tienen streaming de primera clase. La respuesta `Sse` de Axum envuelve un `futures::Stream` y emite fragmentos `text/event-stream`. El formato de la trama es `data: { ... }\n\n` por cuadro, terminado por el cierre de la conexión o un evento `done` explícito.

Una solicitud que consume esto se ve igual que cualquier GET, pero el panel de respuesta en Apidog cambia a modo streaming cuando el `Content-Type` es `text/event-stream`. Ves cada fotograma a medida que llega, con marcas de tiempo. Esa es la vista que necesitas al depurar un problema de contrapresión o un `flush` faltante.

Qué afirmar:

Si tu endpoint utiliza WebSockets en lugar de SSE, Apidog tiene un tipo de solicitud WebSocket separado. El patrón es el mismo: construye la conexión una vez, guarda la secuencia de mensajes, afirma las respuestas.

Paso 7: Simular la API de Rust para el desarrollo frontend en paralelo

El frontend rara vez se bloquea por los tiempos de compilación de Rust. Se bloquea por handlers que aún no existen. Las simulaciones de Apidog te permiten publicar una URL estable que devuelve el contrato acordado entre tú y el frontend, antes de que el handler se implemente.

Haz clic derecho en create-user, elige Smart Mock y actívalo. Apidog ahora sirve una respuesta User sintética en https://mock.apidog.com/m1/<projectId>/users. El cuerpo de la simulación coincide con tu ejemplo guardado. La URL de la simulación acepta la misma forma de cuerpo, por lo que el frontend puede hacer POST contra ella como si fuera el servidor Rust real.

Para simulaciones dinámicas, cambia a Advanced Mock y escribe un script:

return {
  id: Math.floor(Math.random() * 10000),
  name: body.name,
  email: body.email,
  createdAt: new Date().toISOString()
};

Esa simulación responde a lo que el frontend envíe, con un id y una marca de tiempo generados. Cuando el handler Rust esté listo, el frontend cambia su URL base de nuevo a http://localhost:3000 y nada más cambia. Para más información sobre este patrón, el equipo también cubre cómo construir y probar una API de Spring Boot y el flujo de trabajo general de prueba de API; la misma idea, diferentes runtimes.

Paso 8: Guardar como un escenario de prueba de CI

Los Escenarios de Prueba de Apidog encadenan solicitudes con variables compartidas y las ejecutan sin interfaz gráfica. Construye un escenario:

  1. health-check, afirma 200.
  2. create-user, afirma 200, captura body.id en una variable.
  3. create-user-missing-email, afirma 422.
  4. me (con la pre-solicitud JWT), afirma 200 y que el id devuelto coincide con el id capturado.
  5. Solicitud SSE, afirma que el flujo se completa en 5 segundos.

Exporta el escenario como JSON, confírmalo en tu repositorio bajo tests/apidog/, y llámalo desde CI:

- name: Run API contract tests
  run: |
    cargo build --release
    ./target/release/myserver &
    sleep 2
    apidog-cli run tests/apidog/contract.json --env "Rust Local"

Cada PR que modifique un handler ahora se ejecuta contra un binario Rust en vivo con el conjunto completo de contratos. Si un cambio de nombre de Serde, un cambio de código de estado o un ajuste en la validación de JWT rompe la forma pública, CI lo detecta antes de que el botón de fusión se ponga en verde.

Paso 9: Generar OpenAPI a partir de las solicitudes guardadas

Cuando el conjunto de solicitudes sea estable, abre el menú de Exportación de Apidog y elige OpenAPI 3.1. Obtendrás un documento de especificación que cubre cada solicitud guardada, con los cuerpos que enviaste como ejemplos. Entrégalo a cualquiera que genere un cliente tipado (TypeScript, Swift, Kotlin, Python) y obtendrán un contrato que coincide con lo que tu servidor Rust devuelve hoy, no con lo que alguien escribió manualmente en un .yaml hace seis meses.

Si quieres que la especificación se registre en tu repositorio Rust, ejecuta apidog-cli export desde CI y escríbela en openapi.json. El siguiente cargo build no cambia, pero cada consumidor de tu API obtiene la verdad en disco.

Preguntas Frecuentes

¿Funciona Apidog tanto con Axum como con Actix-web? Sí. Apidog habla HTTP, no Rust. Cualquier cosa que responda a una solicitud (Axum, Actix-web, Rocket, Warp, Poem, Loco) funciona de la misma manera. La única consideración específica de Rust es enlazar a 0.0.0.0 en lugar de 127.0.0.1 para las pruebas locales.

¿Cómo pruebo handlers que entran en pánico (panic)? Ejecuta tu servidor con el CatchPanicLayer de tower-http delante del router. El pánico se convierte en un 500 con un cuerpo JSON. Construye una solicitud de Apidog que active la ruta del pánico y afirma el 500. Si no envuelves los pánicos, la conexión se cae y Apidog informa un error de red, lo cual también es una prueba de contrato válida.

¿Puedo ejecutar Apidog contra un binario de Rust en Docker? Sí. Apunta baseUrl al puerto expuesto del contenedor y listo. Si el contenedor se ejecuta dentro de Docker Compose, dale a tu ejecutor de Apidog la misma red o usa el puerto mapeado del host.

¿Qué hay de gRPC? Apidog tiene un tipo de solicitud gRPC. Importa tus archivos .proto, elige un servicio y método, rellena la carga útil de la solicitud y envíala. El patrón de autenticación, entornos y escenarios de prueba es idéntico al de REST.

¿El escenario de prueba reemplaza a cargo test? No. Las pruebas unitarias de tu código Rust se mantienen en Rust. Apidog prueba la superficie en ejecución: el contrato HTTP. Las dos capas detectan errores diferentes. Una prueba unitaria detecta una función rota; una prueba de Apidog detecta una forma de respuesta rota, un encabezado CORS faltante o un 400 que se convirtió en 422. Quieres ambos.

¿Es Apidog gratuito para proyectos de código abierto de Rust? Sí. El cliente de Apidog es gratuito para individuos y equipos pequeños. Los escenarios de prueba, las simulaciones y la exportación de OpenAPI forman parte del nivel gratuito. Si mantienes una API pública de Rust, puedes incluir el archivo del proyecto en tu repositorio para que cualquiera que lo clone obtenga el conjunto de pruebas.

Conclusión

Las APIs de Rust merecen un ciclo de retroalimentación que no espere al compilador. Una colección de solicitudes en Apidog te proporciona ese ciclo: HTTP real, aserciones reales, simulaciones reales para el frontend y un escenario de CI que se ejecuta contra el binario en vivo. Construye las solicitudes anteriores una vez, y cada cambio futuro en tu handler de Axum o Actix se convierte en una ejecución de prueba controlada en lugar de una sorpresa en tiempo de ejecución.

Descarga Apidog y apúntalo a tu servidor Rust. La configuración lleva menos de diez minutos. La recompensa es un contrato que controlas, desacoplado de cargo, y un equipo de frontend que deja de preguntar cuándo estará listo el handler.

Practica el diseño de API en Apidog

Descubre una forma más fácil de construir y usar APIs