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
- Ejecuta tu servidor Rust localmente (
cargo runcontra un proyecto Axum o Actix-web), añade la URL basehttp://localhost:3000como un entorno de Apidog, y guarda cualquier secreto como variables. - Construye la primera solicitud contra
GET /healthz, guárdala y reutiliza la autenticación y la URL base en cada handler de la carpeta. - Para endpoints JSON, pega tu struct de Serde en el editor de cuerpo de Apidog y afirma la forma de la respuesta con scripts de prueba que se ejecutan después de cada envío.
- Para rutas protegidas, crea un JWT una vez, guárdalo como
{{token}}y aplica la autenticación Bearer a nivel de carpeta para que cada prueba la herede. - Simula handlers Rust sin terminar en Apidog para que tu equipo de frontend pueda renderizar respuestas reales antes de que el handler compile limpiamente.
- Guarda el conjunto de trabajo como un Escenario de Prueba, expórtalo y ejecútalo en CI con
apidog-cli. Cada PR que modifique un handler ahora recibe una verificación de contrato antes de la fusión.
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:
- Las verificaciones de contrato se desacoplan de la compilación. Apidog se ejecuta contra un binario en ejecución. Dejas de esperar a
rustcpara validar que tu endpoint sigue devolviendo200. - 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".
- 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
utoipaoaideen 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:
- Método:
GET - URL:
{{baseUrl}}/healthz
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:
- Método:
POST - URL:
{{baseUrl}}/users - Cuerpo (JSON):
{
"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:
- El primer fragmento llega dentro del SLA que anuncias. Apidog muestra la latencia por fragmento en el panel de streaming.
- Un nombre de evento específico se dispara antes del cierre de la conexión (por ejemplo,
event: done). - La duración total del flujo está limitada. Un handler que olvida salir de un
while let Some(event) = stream.next().awaittransmitirá para siempre; Apidog lo mostrará y puedes establecer un tiempo de espera estricto en la configuración de la solicitud para que la prueba falle.
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:
health-check, afirma200.create-user, afirma200, capturabody.iden una variable.create-user-missing-email, afirma422.me(con la pre-solicitud JWT), afirma200y que eliddevuelto coincide con elidcapturado.- 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.
