Comment Tester l'API Rust ?

INEZA Felin-Michel

INEZA Felin-Michel

13 May 2026

Comment Tester l'API Rust ?

enterprise.banner.title

enterprise.banner.feature1

enterprise.banner.feature2

enterprise.banner.feature3

enterprise.banner.ctaB

Rust vous offre un serveur HTTP rapide et sûr en termes de types en quelques centaines de lignes. Ce qu'il ne vous offre pas, c'est une boucle de rétroaction rapide pour tester ce serveur. Le cycle de compilation est long, cargo test relance tout à chaque changement de trait, et la plupart des frameworks HTTP Rust vous obligent à écrire un test d'intégration distinct pour chaque endpoint avant même de l'avoir appelé une seule fois. Si vous voulez livrer une API et pas seulement un binaire, vous avez besoin d'un outil qui vit en dehors de la chaîne d'outils Rust et qui communique avec le serveur en cours d'exécution.

Ce guide décrit le workflow complet de test d'API Rust à l'intérieur d'Apidog : pointer Apidog vers votre serveur Axum ou Actix, construire des requêtes vers vos endpoints, valider le JSON sérialisé par Serde, gérer l'authentification JWT, simuler des endpoints pour que le frontend puisse avancer pendant que vous terminez le handler, et empaqueter le tout comme un scénario de test CI. À la fin, vous aurez un projet Apidog réutilisable qui détecte les dérives de contrat avant que cargo build --release ne se termine.

Si vous venez d'un workflow Postman ou curl, vous bénéficiez également gratuitement des fonctionnalités de conception d'Apidog : une spécification OpenAPI générée à partir de vos requêtes enregistrées, des URL de mock partageables et des environnements d'équipe. L'histoire de la migration depuis Postman est à lire séparément ; cet article se concentre sur Rust.

TL;DR

Pourquoi tester une API Rust en dehors de la chaîne d'outils Rust

cargo test est bien. Il est aussi lent, opaque pour les coéquipiers non-Rust, et construit autour du code plutôt que du HTTP. Si vous voulez vérifier que votre handler renvoie le bon code de statut, la bonne forme JSON, les bons en-têtes et le bon message d'erreur lorsque l'entrée est mal formée, vous écrivez un nouvel appel tower::ServiceExt::oneshot pour chaque cas. Ensuite, vous maintenez ce test à mesure que le handler change. Ensuite, vous l'écrivez à nouveau en JavaScript pour que le frontend puisse utiliser un mock.

Apidog vous offre une couche de contrat unique au-dessus du serveur en cours d'exécution. La requête ne vit qu'une fois. Les assertions vivent à côté de la requête. Les coéquipiers frontend ouvrent le même projet et voient les mêmes requêtes que vous. Lorsque Serde obtient un attribut #[serde(rename_all = "camelCase")] dans trois semaines, le test qui échoue est celui d'Apidog, pas celui qui est livré en production.

Trois raisons concrètes d'ajouter Apidog à un workflow Rust :

  1. Les vérifications de contrat sont découplées de la construction. Apidog s'exécute sur un binaire en cours d'exécution. Vous n'attendez plus rustc pour valider que votre endpoint renvoie toujours 200.
  2. Les mocks sont partageables. Un développeur frontend dans un autre fuseau horaire reçoit une URL qui renvoie le bon JSON, pas un message Slack disant "le handler n'est pas encore terminé".
  3. OpenAPI gratuit. Apidog peut générer un document OpenAPI 3.1 à partir des requêtes enregistrées. Vous le donnez à quiconque souhaite un client typé sans écrire d'annotation utoipa ou aide sur chaque route.

Étape 1 : Ajoutez votre serveur Rust comme environnement Apidog

Démarrez votre API Rust. Pour un projet Axum, le boilerplate est :

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();
}

Ouvrez Apidog, créez un nouveau projet, puis ouvrez la Gestion de l'environnement (menu déroulant en haut à droite) et ajoutez un environnement appelé Rust Local :

Variable Valeur
baseUrl http://localhost:3000
token laisser vide pour l'instant
apiVersion v1

Ajoutez un deuxième environnement appelé Rust Staging avec l'URL de base déployée. Apidog scope les variables par environnement, vous passez donc du local au staging en un clic de menu déroulant. Pas de recherche et remplacement dans les requêtes enregistrées.

Étape 2 : Appelez le premier endpoint

Créez un dossier appelé Rust API dans le projet, puis une nouvelle requête :

Cliquez sur Envoyer. Si votre serveur tourne, vous obtenez un 200 avec le corps ok. Enregistrez-le comme health-check. C'est le test de fumée le plus simple possible, et il confirme que l'environnement et l'URL de base fonctionnent avant que vous n'écriviez quoi que ce soit de plus intéressant.

Si vous obtenez une erreur de connexion refusée, votre serveur n'est pas lié à 0.0.0.0 ou le port est incorrect. Le TcpListener::bind("127.0.0.1:3000") par défaut de Rust rejettera les requêtes provenant de tout ce qui se résout en localhost sur une interface différente ; liez-vous à 0.0.0.0 pour le développement local afin qu'Apidog et les conteneurs Docker puissent l'atteindre.

Étape 3 : Testez les requêtes et réponses JSON avec Serde

La forme d'API Rust la plus courante est un handler JSON-in, JSON-out, soutenu par une structure Serde. Ajoutez une route 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));

Dans Apidog, créez une requête :

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

Envoyez-le. Vous obtenez en retour le JSON User. Enregistrez-le comme create-user.

Maintenant, ouvrez l'onglet Tests et ajoutez des assertions :

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 prochaine fois que quelqu'un ajoutera #[serde(rename_all = "camelCase")] à la structure et que la forme de votre réponse passera de user_id à userId, ce test échouera avant que le changement ne soit livré. C'est le contrat qu'Apidog vous donne et que cargo test ne vous donne pas, car cargo test exécute votre code Rust sur vos types Rust et passerait sans problème avec l'une ou l'autre forme.

Étape 4 : Couvrez les cas de rejet de Serde

La partie intéressante de la gestion du JSON avec Rust est ce que Serde fait avec les mauvaises entrées. Par défaut, Axum renvoie un 422 Unprocessable Entity sans détails. Construisez trois requêtes qui violent intentionnellement le schéma :

Requête Corps 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

Affirmez chaque code de statut dans les Tests. C'est le moyen le moins cher de documenter votre véritable politique de validation. Si vous activez deny_unknown_fields plus tard, le deuxième test deviendra rouge et vous indiquera que le contrat public a changé.

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

La plupart des API Rust en production masquent les handlers derrière un middleware d'authentification. L'extracteur JWT axum-extra d'Axum est le pattern courant :

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() }))
}

Dans Apidog, vous n'avez pas besoin de créer manuellement un JWT à chaque exécution de test. Créez un script de pré-requête sur le dossier :

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

Ouvrez les paramètres du dossier, définissez l'Auth sur Bearer Token, valeur {{token}}. Chaque requête du dossier signe et présente désormais un nouveau JWT. Les bugs de jeton expiré disparaissent de vos exécutions de test. Pour une explication plus détaillée du côté de l'assertion, consultez comment tester l'authentification JWT dans les API.

Étape 6 : Tester le streaming et les événements Server-Sent

Les frameworks web Rust ont un streaming de première classe. La réponse Sse d'Axum enveloppe un futures::Stream et émet des chunks text/event-stream. Le format filaire est data: { ... }\n\n par frame, terminé par la fermeture de la connexion ou un événement "done" explicite.

Une requête qui consomme ceci ressemble à n'importe quel GET, mais le panneau de réponse dans Apidog passe en mode streaming lorsque le Content-Type est text/event-stream. Vous voyez chaque frame au fur et à mesure de son arrivée, avec des horodatages. C'est la vue dont vous avez besoin lors du débogage d'un problème de contre-pression ou d'un flush manquant.

Quoi affirmer :

Si votre endpoint utilise des WebSockets au lieu de SSE, Apidog dispose d'un type de requête WebSocket distinct. Le modèle est le même : construire la connexion une fois, enregistrer la séquence de messages, affirmer les réponses.

Étape 7 : Simulez l'API Rust pour le développement frontend parallèle

Le frontend est rarement bloqué par les temps de compilation de Rust. Il est bloqué par des handlers qui n'existent pas encore. Les mocks Apidog vous permettent de publier une URL stable qui renvoie le contrat que vous et le frontend avez convenu, avant que le handler ne soit livré.

Faites un clic droit sur create-user, choisissez Smart Mock, et activez-le. Apidog sert maintenant une réponse User synthétique à l'adresse https://mock.apidog.com/m1/<projectId>/users. Le corps du mock correspond à votre exemple enregistré. L'URL du mock accepte la même forme de corps, de sorte que le frontend peut envoyer une requête POST comme s'il s'agissait du vrai serveur Rust.

Pour les mocks dynamiques, passez à Advanced Mock et écrivez un script :

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

Ce mock répond à tout ce que le frontend envoie, avec un id et un horodatage générés. Lorsque le handler Rust est prêt, le frontend réinitialise son URL de base à http://localhost:3000 et rien d'autre ne change. Pour en savoir plus sur ce modèle, l'équipe couvre également la création et le test d'une API Spring Boot et le workflow général de test d'API ; même idée, runtimes différents.

Étape 8 : Enregistrez comme scénario de test CI

Les scénarios de test Apidog enchaînent les requêtes avec des variables partagées et les exécutent sans interface graphique. Construisez un scénario :

  1. health-check, affirmez 200.
  2. create-user, affirmez 200, capturez body.id dans une variable.
  3. create-user-missing-email, affirmez 422.
  4. me (avec la pré-requête JWT), affirmez 200 et l'id renvoyé correspond à l'id capturé.
  5. Requête SSE, affirmez que le flux se termine dans les 5 secondes.

Exportez le scénario en JSON, commitez-le dans votre dépôt sous tests/apidog/, et appelez-le depuis la 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"

Chaque PR qui touche un handler s'exécute désormais sur un binaire Rust en direct avec la suite de contrats complète. Si un renommage Serde, un changement de code de statut ou un ajustement de validation JWT rompt la forme publique, la CI le détecte avant que le bouton de fusion ne devienne vert.

Étape 9 : Générez OpenAPI à partir des requêtes enregistrées

Lorsque l'ensemble de requêtes est stable, ouvrez le menu d'exportation d'Apidog et choisissez OpenAPI 3.1. Vous obtenez un document de spécification couvrant chaque requête enregistrée, avec les corps que vous avez envoyés comme exemples. Remettez cela à quiconque génère un client typé (TypeScript, Swift, Kotlin, Python) et il obtiendra un contrat qui correspond à ce que votre serveur Rust renvoie aujourd'hui, et non à ce que quelqu'un a écrit à la main dans un fichier .yaml il y a six mois.

Si vous souhaitez que la spécification soit vérifiée dans votre dépôt Rust, exécutez apidog-cli export depuis la CI et écrivez-la dans openapi.json. Le prochain cargo build ne change pas, mais chaque consommateur de votre API obtient la vérité sur disque.

FAQ

Apidog fonctionne-t-il avec Axum et Actix-web ? Oui. Apidog parle HTTP, pas Rust. Tout ce qui répond à une requête (Axum, Actix-web, Rocket, Warp, Poem, Loco) fonctionne de la même manière. La seule considération spécifique à Rust est de se lier à 0.0.0.0 au lieu de 127.0.0.1 pour les tests locaux.

Comment tester les handlers qui paniquent ? Exécutez votre serveur avec tower-http’s CatchPanicLayer devant le routeur. La panique se transforme en 500 avec un corps JSON. Créez une requête Apidog qui déclenche le chemin de panique et affirmez le 500. Si vous n'enveloppez pas les paniques, la connexion tombe et Apidog signale une erreur réseau, ce qui est également un test de contrat valide.

Puis-je exécuter Apidog contre un binaire Rust dans Docker ? Oui. Pointez baseUrl vers le port exposé du conteneur et c'est tout. Si le conteneur s'exécute dans Docker Compose, donnez à votre exécuteur Apidog le même réseau ou utilisez le port mappé de l'hôte.

Qu'en est-il du gRPC ? Apidog a un type de requête gRPC. Importez vos fichiers .proto, choisissez un service et une méthode, remplissez la charge utile de la requête et envoyez. Le modèle d'authentification, d'environnements et de scénarios de test est identique à REST.

Le scénario de test remplace-t-il cargo test ? Non. Les tests unitaires pour votre code Rust restent en Rust. Apidog teste la surface d'exécution : le contrat HTTP. Les deux couches détectent des bugs différents. Un test unitaire détecte une fonction cassée ; un test Apidog détecte une forme de réponse cassée, un en-tête CORS manquant, ou un 400 qui est devenu un 422. Vous voulez les deux.

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'exportation OpenAPI font partie du niveau gratuit. Si vous maintenez une API Rust publique, vous pouvez livrer le fichier de projet dans votre dépôt afin que quiconque le clone obtienne la suite de tests.

En résumé

Les API Rust méritent une boucle de rétroaction qui n'attend pas le compilateur. Une collection de requêtes dans Apidog vous offre cette boucle : de vrais HTTP, de vraies assertions, de vrais mocks pour le frontend, et un scénario CI qui s'exécute sur le binaire en direct. Construisez les requêtes ci-dessus une seule fois, et chaque futur changement à votre handler Axum ou Actix deviendra une exécution de test contrôlée au lieu d'une surprise à l'exécution.

Téléchargez Apidog et pointez-le vers votre serveur Rust. La configuration prend moins de dix minutes. Le résultat est un contrat que vous contrôlez, découplé de cargo, et une équipe frontend qui ne demande plus quand le handler sera terminé.

Pratiquez le Design-first d'API dans Apidog

Découvrez une manière plus simple de créer et utiliser des API