DEV Community

Cover image for Comment Tester l'API Rust ?
Antoine Laurent
Antoine Laurent

Posted on • Originally published at apidog.com

Comment Tester l'API Rust ?

Rust permet de créer un serveur HTTP rapide et sûr en quelques centaines de lignes. Le point faible arrive souvent après : obtenir une boucle de feedback rapide pour tester les endpoints, les statuts HTTP, les payloads JSON et l’authentification sans relancer toute la chaîne Rust à chaque modification. Pour livrer une API, pas seulement un binaire, ajoutez une couche de test HTTP externe qui parle au serveur en cours d’exécution.

Essayez Apidog aujourd’hui

Ce guide montre un workflow complet de test d’API Rust avec Apidog : connecter un serveur Axum ou Actix, créer des requêtes réutilisables, valider le JSON produit par Serde, gérer un JWT, mocker des endpoints pour le frontend, puis exécuter le tout en CI.

Si vous venez d’un workflow Postman ou curl, Apidog ajoute aussi une couche de conception : spécification OpenAPI générée depuis les requêtes enregistrées, URL de mock partageables et environnements d’équipe. La migration depuis Postman est un sujet séparé ; ici, on se concentre sur Rust.

TL;DR

  • Lancez votre serveur Rust localement (cargo run) et configurez http://localhost:3000 comme baseUrl dans Apidog.
  • Créez une première requête GET /healthz pour valider l’environnement.
  • Ajoutez vos endpoints JSON, puis écrivez des assertions sur les statuts et la forme des réponses.
  • Stockez le JWT dans une variable {{token}} et appliquez l’authentification Bearer au niveau du dossier.
  • Mockez les handlers Rust non terminés pour débloquer le frontend.
  • Regroupez les requêtes dans un scénario de test et lancez-le en CI avec apidog-cli.

Pourquoi tester une API Rust en dehors de cargo test

cargo test reste indispensable pour les tests unitaires et d’intégration Rust. Mais pour vérifier le contrat HTTP réel — code de statut, headers, forme JSON, messages d’erreur, auth, CORS — il devient vite verbeux.

Avec Apidog, vous testez le serveur tel qu’il tourne :

  1. Les tests de contrat sont découplés de la compilation.

    Vous exécutez des requêtes HTTP contre un binaire déjà lancé.

  2. Les mocks sont partageables.

    Le frontend peut consommer une URL stable même si le handler Rust n’est pas encore prêt.

  3. OpenAPI peut être généré depuis les requêtes.

    Vous obtenez une spec OpenAPI 3.1 basée sur ce que vous testez réellement, sans annoter toutes vos routes avec utoipa ou aide.

Étape 1 : Ajouter le serveur Rust comme environnement Apidog

Démarrez une API Axum minimale :

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();
}
Enter fullscreen mode Exit fullscreen mode

Dans Apidog :

  1. Créez un nouveau projet.
  2. Ouvrez la gestion des environnements.
  3. Ajoutez un environnement Rust Local.
Variable Valeur
baseUrl http://localhost:3000
token laisser vide pour l’instant
apiVersion v1

Ajoutez aussi un environnement Rust Staging avec l’URL de staging. Les variables étant scopées par environnement, vous pouvez passer du local au staging sans modifier les requêtes.

Étape 2 : Créer le premier test HTTP

Créez un dossier Rust API, puis une requête :

  • Méthode : GET
  • URL : {{baseUrl}}/healthz

Envoyez la requête. Le serveur doit répondre :

ok
Enter fullscreen mode Exit fullscreen mode

avec un statut :

200 OK
Enter fullscreen mode Exit fullscreen mode

Enregistrez la requête sous le nom health-check.

Si vous obtenez connection refused, vérifiez :

  • que le serveur tourne ;
  • que le port est bien 3000 ;
  • que le serveur écoute sur 0.0.0.0:3000 plutôt que seulement 127.0.0.1:3000.

Cette requête devient votre test de fumée : si elle échoue, inutile de lancer le reste de la suite.

Étape 3 : Tester un endpoint JSON avec Serde

Ajoutez un endpoint POST /users côté Rust :

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));
Enter fullscreen mode Exit fullscreen mode

Dans Apidog, créez une requête :

  • Méthode : POST
  • URL : {{baseUrl}}/users
  • Body JSON :
{
  "name": "Ada Lovelace",
  "email": "ada@example.com"
}
Enter fullscreen mode Exit fullscreen mode

Envoyez la requête et enregistrez-la sous create-user.

Ajoutez ensuite des assertions dans l’onglet Tests :

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(/^[^@]+@[^@]+$/);
});
Enter fullscreen mode Exit fullscreen mode

Ces assertions protègent le contrat public. Si vous changez plus tard un attribut Serde, par exemple avec #[serde(rename_all = "camelCase")], la suite Apidog détectera la dérive de réponse.

Étape 4 : Couvrir les erreurs de validation Serde

Testez aussi les entrées invalides. Créez trois requêtes à partir de create-user.

Requête Body Attendu
create-user-missing-email { "name": "Ada" } 422, le corps mentionne missing field email
create-user-extra-field { "name": "Ada", "email": "a@b.c", "admin": true } 200 si #[serde(deny_unknown_fields)] est absent ; 422 sinon
create-user-wrong-type { "name": 1, "email": "a@b.c" } 422, mentionne invalid type: integer

Exemple d’assertion pour le champ manquant :

pm.test("Rejects missing email", () => {
  pm.expect(pm.response.code).to.eql(422);
  pm.expect(pm.response.text()).to.include("missing field");
});
Enter fullscreen mode Exit fullscreen mode

Ce type de test documente votre politique de validation réelle. Si vous activez deny_unknown_fields plus tard, le test correspondant échouera et signalera un changement de contrat.

Étape 5 : Tester les routes protégées par JWT

Les API Rust en production exposent souvent des routes derrière un middleware d’authentification. Exemple avec un extracteur basé sur cookie et jsonwebtoken :

use axum::{Json, http::StatusCode};
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(),
    }))
}
Enter fullscreen mode Exit fullscreen mode

Dans Apidog, évitez de générer le JWT manuellement. Ajoutez un script de pré-requête au niveau du dossier :

const jwt = require("jsonwebtoken");

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

pm.environment.set("token", token);
Enter fullscreen mode Exit fullscreen mode

Puis configurez l’authentification du dossier :

  • Type : Bearer Token
  • Token : {{token}}

Toutes les requêtes du dossier héritent maintenant du JWT.

Pour tester le cas non autorisé, créez une requête séparée qui désactive l’auth ou remplace {{token}} par une valeur invalide, puis ajoutez :

pm.test("Rejects invalid token", () => {
  pm.expect(pm.response.code).to.eql(401);
});
Enter fullscreen mode Exit fullscreen mode

Pour approfondir ce sujet, consultez comment tester l’authentification JWT dans les API.

Étape 6 : Tester le streaming et les Server-Sent Events

Les frameworks web Rust gèrent bien le streaming. Avec Axum, une réponse Sse encapsule un futures::Stream et émet des chunks text/event-stream.

Le format réseau ressemble à ceci :

data: { ... }

data: { ... }

event: done
data: {}
Enter fullscreen mode Exit fullscreen mode

Dans Apidog :

  1. Créez une requête GET vers votre endpoint SSE.
  2. Envoyez-la.
  3. Vérifiez que le panneau de réponse passe en mode streaming lorsque le Content-Type vaut text/event-stream.

À vérifier pendant le test :

  • le premier chunk arrive dans le délai attendu ;
  • un événement final comme event: done est bien reçu ;
  • le flux ne reste pas ouvert indéfiniment ;
  • le timeout de la requête est assez strict pour faire échouer un stream bloqué.

Si votre API utilise WebSocket plutôt que SSE, utilisez le type de requête WebSocket d’Apidog. Le principe reste identique : ouvrir la connexion, enregistrer la séquence de messages, vérifier les réponses.

Étape 7 : Mocker l’API Rust pour débloquer le frontend

Les mocks Apidog permettent au frontend d’avancer avant que le handler Rust ne soit terminé.

À partir de la requête create-user :

  1. Faites un clic droit sur la requête.
  2. Activez Smart Mock.
  3. Partagez l’URL générée, par exemple :
https://mock.apidog.com/m1/<projectId>/users
Enter fullscreen mode Exit fullscreen mode

Le mock renvoie une réponse basée sur l’exemple enregistré.

Pour un mock dynamique, utilisez Advanced Mock avec un script :

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

Le frontend peut alors envoyer un POST avec le même body que celui attendu par le serveur Rust. Quand le vrai handler est prêt, il suffit de remplacer l’URL de base par http://localhost:3000.

Le même workflow est présenté dans les articles sur la création et le test d’une API Spring Boot et le workflow général de test d’API.

Étape 8 : Créer un scénario de test CI

Un scénario de test Apidog enchaîne plusieurs requêtes avec des variables partagées.

Créez un scénario avec cet ordre :

  1. health-check

    • assert 200
  2. create-user

    • assert 200
    • capturer body.id dans une variable
  3. create-user-missing-email

    • assert 422
  4. me

    • utiliser le script JWT de pré-requête
    • assert 200
    • vérifier que l’id renvoyé correspond à l’id capturé si votre logique métier le permet
  5. Requête SSE

    • vérifier que le flux se termine dans les 5 secondes

Exemple de capture d’id :

pm.test("Capture user id", () => {
  const body = pm.response.json();

  pm.expect(body).to.have.property("id");

  pm.environment.set("userId", body.id);
});
Enter fullscreen mode Exit fullscreen mode

Exportez ensuite le scénario en JSON et versionnez-le dans le dépôt :

tests/apidog/contract.json
Enter fullscreen mode Exit fullscreen mode

Exemple GitHub Actions :

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

Chaque pull request qui modifie un handler est alors testée contre un vrai binaire Rust. Si un renommage Serde, un changement de statut HTTP ou une régression JWT casse le contrat, la CI échoue avant la fusion.

Étape 9 : Générer OpenAPI depuis les requêtes enregistrées

Quand les requêtes sont stables :

  1. Ouvrez le menu d’export Apidog.
  2. Choisissez OpenAPI 3.1.
  3. Exportez le document.

Vous obtenez une spécification basée sur les requêtes et réponses testées. Elle peut ensuite servir à générer des clients typés TypeScript, Swift, Kotlin ou Python.

Si vous voulez versionner la spec dans le dépôt Rust, exécutez l’export en CI :

apidog-cli export --format openapi --output openapi.json
Enter fullscreen mode Exit fullscreen mode

Le build Rust ne change pas, mais les consommateurs de l’API disposent d’un contrat à jour.

FAQ

Apidog fonctionne-t-il avec Axum et Actix-web ?

Oui. Apidog parle HTTP, pas Rust. Axum, Actix-web, Rocket, Warp, Poem ou Loco fonctionnent de la même manière tant qu’ils exposent un endpoint HTTP.

La seule attention côté local : liez le serveur à 0.0.0.0 si Apidog, Docker ou d’autres outils doivent y accéder.

Comment tester les handlers qui paniquent ?

Ajoutez CatchPanicLayer de tower-http devant le routeur. La panique peut alors devenir une réponse 500 testable.

Sinon, la connexion sera coupée et Apidog signalera une erreur réseau. C’est aussi un signal utile si votre contrat interdit les interruptions brutales.

Puis-je tester un binaire Rust dans Docker ?

Oui. Pointez baseUrl vers le port exposé du conteneur.

Avec Docker Compose, utilisez soit :

  • le port mappé sur l’hôte ;
  • le même réseau Docker que celui utilisé par l’exécuteur Apidog.

Qu’en est-il du gRPC ?

Apidog dispose d’un type de requête gRPC. Importez vos fichiers .proto, choisissez le service et la méthode, remplissez la payload, puis envoyez la requête.

Les environnements, l’authentification et les scénarios de test suivent le même modèle que pour REST.

Le scénario Apidog remplace-t-il cargo test ?

Non.

Gardez les tests unitaires et d’intégration Rust dans cargo test. Utilisez Apidog pour tester la surface d’exécution HTTP :

  • forme JSON ;
  • statuts HTTP ;
  • headers ;
  • erreurs de validation ;
  • authentification ;
  • mocks ;
  • streaming.

Les deux couches détectent des bugs différents.

Apidog est-il gratuit pour les projets open-source Rust ?

Oui. Le client Apidog est gratuit pour les particuliers et les petites équipes. Les scénarios de test, les mocks et l’export OpenAPI font partie du niveau gratuit.

En résumé

Une API Rust a besoin de tests proches du protocole HTTP réel. Avec Apidog, vous pouvez créer une collection de requêtes, ajouter des assertions, générer des mocks, exporter OpenAPI et exécuter le contrat en CI contre un binaire Rust vivant.

Téléchargez Apidog, pointez-le vers votre serveur Rust, puis commencez par GET /healthz. En quelques requêtes, vous obtenez un contrat testable, partageable et découplé de cargo.

Top comments (0)