Rust memberi Anda server HTTP yang cepat dan aman tipe (type-safe) dalam beberapa ratus baris kode. Namun, yang tidak diberikannya adalah siklus umpan balik yang cepat untuk menguji server tersebut. Siklus kompilasi panjang, `cargo test` menjalankan ulang semuanya hanya karena perubahan kecil pada trait, dan sebagian besar kerangka kerja HTTP Rust mengharuskan Anda menulis pengujian integrasi terpisah untuk setiap endpoint bahkan sebelum Anda pernah memanggilnya sekali. Jika Anda ingin meluncurkan sebuah API dan bukan hanya sebuah biner, Anda membutuhkan alat yang berada di luar toolchain Rust dan berkomunikasi dengan server yang sedang berjalan.
Panduan ini membahas alur kerja pengujian API Rust secara lengkap di dalam Apidog: mengarahkan Apidog ke server Axum atau Actix Anda, membangun permintaan terhadap endpoint Anda, memvalidasi JSON yang diserialisasi Serde, menangani autentikasi JWT, membuat mock endpoint agar frontend dapat bergerak maju sementara Anda menyelesaikan handler, dan mengemas semuanya sebagai skenario pengujian CI. Pada akhirnya, Anda akan memiliki proyek Apidog yang dapat digunakan kembali yang menangkap "contract drift" sebelum `cargo build --release` selesai.
Jika Anda berasal dari alur kerja Postman atau `curl`, Anda juga mendapatkan fitur desain-pertama Apidog secara gratis: spesifikasi OpenAPI yang dihasilkan dari permintaan yang disimpan, URL mock yang dapat dibagikan, dan lingkungan tim. Simpan kisah migrasi Postman untuk bacaan terpisah; postingan ini tetap berfokus pada Rust.
TL;DR
- Jalankan server Rust Anda secara lokal (`cargo run` terhadap proyek Axum atau Actix-web), tambahkan URL dasar `http://localhost:3000` sebagai lingkungan Apidog, dan simpan rahasia apa pun sebagai variabel.
- Buat permintaan pertama terhadap `GET /healthz`, simpan, dan gunakan kembali autentikasi serta URL dasar di setiap handler dalam folder.
- Untuk endpoint JSON, tempelkan struct Serde Anda ke editor body Apidog dan pastikan bentuk respons dengan skrip Tests yang berjalan setelah setiap pengiriman.
- Untuk rute yang dilindungi, cetak JWT sekali, simpan sebagai `{{token}}`, dan terapkan otentikasi Bearer di tingkat folder sehingga setiap pengujian mewarisinya.
- Mock handler Rust yang belum selesai di Apidog sehingga tim frontend Anda dapat merender respons nyata sebelum handler berhasil dikompilasi.
- Simpan set kerja sebagai Skenario Pengujian, ekspor, dan jalankan di CI dengan `apidog-cli`. Setiap PR yang menyentuh handler sekarang mendapatkan pemeriksaan kontrak sebelum digabungkan.
Mengapa menguji API Rust di luar toolchain Rust
`cargo test` itu bagus. Tapi juga lambat, tidak transparan bagi rekan tim non-Rust, dan dibangun di sekitar kode alih-alih HTTP. Jika Anda ingin memverifikasi bahwa handler Anda mengembalikan kode status yang benar, bentuk JSON yang benar, header yang benar, dan pesan kesalahan yang benar ketika input salah format, Anda menulis panggilan `tower::ServiceExt::oneshot` baru untuk setiap kasus. Kemudian Anda mempertahankan pengujian itu saat handler berubah. Kemudian Anda menulisnya lagi di JavaScript agar frontend dapat mengakses mock.
Apidog memberi Anda satu lapisan kontrak di atas server yang sedang berjalan. Permintaan hanya ada sekali. Asersi (assertions) berada di samping permintaan. Rekan tim frontend membuka proyek yang sama dan melihat permintaan yang sama dengan Anda. Ketika Serde mendapatkan atribut `#[serde(rename_all = "camelCase")]` tiga minggu dari sekarang, pengujian yang gagal adalah yang di Apidog, bukan yang dikirim ke produksi.
Tiga alasan konkret untuk menambahkan Apidog ke alur kerja Rust:
- Pemeriksaan kontrak terlepas dari build. Apidog berjalan terhadap biner yang sedang berjalan. Anda berhenti menunggu `rustc` untuk memvalidasi bahwa endpoint Anda masih mengembalikan `200`.
- Mock dapat dibagikan. Pengembang frontend di zona waktu lain mendapatkan URL yang mengembalikan JSON yang benar, bukan pesan Slack yang mengatakan "handler belum selesai."
- OpenAPI secara gratis. Apidog dapat menghasilkan dokumen OpenAPI 3.1 dari permintaan yang disimpan. Anda memberikannya kepada siapa pun yang menginginkan klien bertipe tanpa menulis anotasi `utoipa` atau `aide` di setiap rute.
Langkah 1: Tambahkan server Rust Anda sebagai lingkungan Apidog
Mulai API Rust Anda. Untuk proyek Axum, boilerplate-nya adalah:
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();
}
Buka Apidog, buat proyek baru, lalu buka Manajemen Lingkungan (dropdown kanan atas) dan tambahkan lingkungan bernama `Rust Local`:
| Variabel | Nilai |
|---|---|
baseUrl |
http://localhost:3000 |
token |
biarkan kosong untuk saat ini |
apiVersion |
v1 |
Tambahkan lingkungan kedua bernama `Rust Staging` dengan URL dasar yang di-deploy. Apidog menetapkan lingkup variabel per lingkungan, jadi Anda beralih dari lokal ke staging dengan satu klik dropdown. Tidak perlu mencari dan mengganti melalui permintaan yang tersimpan.
Langkah 2: Akses endpoint pertama
Buat folder bernama `Rust API` di dalam proyek, lalu permintaan baru:
- Metode: `GET`
- URL: `{{baseUrl}}/healthz`
Kirim. Jika server Anda berjalan, Anda mendapatkan `200` dengan body `ok`. Simpan ini sebagai `health-check`. Ini adalah pengujian asap (smoke test) paling sederhana yang mungkin, dan ini mengonfirmasi bahwa lingkungan dan URL dasar berfungsi sebelum Anda menulis sesuatu yang lebih menarik.
Jika Anda mendapatkan kesalahan "connection refused", server Anda tidak terikat ke `0.0.0.0` atau portnya salah. `TcpListener::bind("127.0.0.1:3000")` bawaan Rust akan menolak permintaan yang berasal dari apa pun yang merujuk ke `localhost` pada antarmuka yang berbeda; ikat ke `0.0.0.0` untuk pengembangan lokal agar Apidog dan container Docker dapat mencapainya.
Langkah 3: Uji permintaan dan respons JSON dengan Serde
Bentuk API Rust yang paling umum adalah handler JSON-masuk, JSON-keluar yang didukung oleh Serde struct. Tambahkan rute `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));
Di Apidog, buat permintaan:
- Metode: `POST`
- URL: `{{baseUrl}}/users`
- Body (JSON):
{
"name": "Ada Lovelace",
"email": "ada@example.com"
}
Kirim. Anda akan mendapatkan kembali JSON `User`. Simpan sebagai `create-user`.
Sekarang buka tab Tests dan tambahkan asersi:
pm.test("Status adalah 200", () => {
pm.expect(pm.response.code).to.eql(200);
});
pm.test("Body memiliki id, nama, 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(/^[^@]+@[^@]+$/);
});
Lain kali seseorang menambahkan `#[serde(rename_all = "camelCase")]` ke struct dan bentuk respons Anda berubah dari `user_id` menjadi `userId`, pengujian ini akan gagal sebelum perubahan diterapkan. Itulah kontrak yang diberikan Apidog kepada Anda yang tidak diberikan `cargo test`, karena `cargo test` menjalankan kode Rust Anda terhadap tipe Rust Anda dan akan dengan senang hati lolos dengan salah satu bentuk.
Langkah 4: Meliputi kasus penolakan Serde
Bagian menarik dari penanganan JSON Rust adalah apa yang Serde lakukan dengan input yang buruk. Secara default, Axum mengembalikan `422 Unprocessable Entity` tanpa detail. Buat tiga permintaan yang sengaja melanggar skema:
| Permintaan | Body | Diharapkan |
|---|---|---|
create-user-missing-email |
{ "name": "Ada" } |
422, body menyebutkan missing field email |
create-user-extra-field |
{ "name": "Ada", "email": "a@b.c", "admin": true } |
200 jika #[serde(deny_unknown_fields)] tidak ada; 422 jika ada |
create-user-wrong-type |
{ "name": 1, "email": "a@b.c" } |
422, menyebutkan invalid type: integer |
Pastikan setiap kode status dalam Tests. Ini adalah cara termurah untuk mendokumentasikan kebijakan validasi Anda yang sebenarnya. Jika Anda mengaktifkan `deny_unknown_fields` nanti, pengujian kedua akan menjadi merah dan memberitahu Anda bahwa kontrak publik berubah.
Langkah 5: Uji rute yang dilindungi JWT
Sebagian besar API Rust produksi menyembunyikan handler di balik middleware otentikasi. Ekstraktor JWT `axum-extra` Axum adalah pola umum:
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() }))
}
Di Apidog, Anda tidak perlu membuat JWT secara manual setiap kali menjalankan pengujian. Buat Skrip Pra-Permintaan pada folder:
const jwt = require("jsonwebtoken");
const token = jwt.sign(
{ sub: 1, exp: Math.floor(Date.now() / 1000) + 3600 },
"secret"
);
pm.environment.set("token", token);
Buka pengaturan folder, atur Auth ke Bearer Token, nilai `{{token}}`. Setiap permintaan dalam folder sekarang menandatangani dan menyajikan JWT baru. Bug token usang hilang dari jalannya pengujian Anda. Untuk panduan yang lebih mendalam tentang sisi asersi, lihat cara menguji autentikasi JWT di API.
Langkah 6: Uji streaming dan Server-Sent Events
Kerangka kerja web Rust memiliki streaming kelas satu. Respons `Sse` Axum membungkus `futures::Stream` dan memancarkan chunk `text/event-stream`. Format wire adalah `data: { ... }\n\n` per frame, diakhiri dengan penutupan koneksi atau event "done" eksplisit.
Permintaan yang mengonsumsi ini terlihat sama dengan GET apa pun, tetapi panel respons di Apidog beralih ke mode streaming ketika `Content-Type` adalah `text/event-stream`. Anda melihat setiap frame saat tiba, dengan stempel waktu. Itulah tampilan yang Anda butuhkan saat men-debug masalah backpressure atau flush yang hilang.
Apa yang harus dipastikan:
- Chunk pertama tiba dalam SLA yang Anda iklankan. Apidog menunjukkan latensi per chunk di panel streaming.
- Nama event tertentu muncul sebelum koneksi ditutup (misalnya, `event: done`).
- Durasi total stream dibatasi. Handler yang lupa untuk keluar dari `while let Some(event) = stream.next().await` akan melakukan streaming selamanya; Apidog akan menunjukkannya dan Anda dapat mengatur batas waktu keras dalam pengaturan permintaan untuk menggagalkan pengujian.
Jika endpoint Anda menggunakan WebSockets alih-alih SSE, Apidog memiliki jenis permintaan WebSocket terpisah. Polanya sama: bangun koneksi sekali, simpan urutan pesan, pastikan responsnya.
Langkah 7: Mock API Rust untuk pengembangan frontend paralel
Frontend jarang terhambat oleh waktu kompilasi Rust. Frontend terhambat oleh handler yang belum ada. Mock Apidog memungkinkan Anda menerbitkan URL stabil yang mengembalikan kontrak yang Anda dan frontend sepakati, sebelum handler diluncurkan.
Klik kanan `create-user`, pilih Smart Mock, dan aktifkan. Apidog sekarang menyajikan respons `User` sintetis di `https://mock.apidog.com/m1/<projectId>/users`. Body mock cocok dengan contoh yang Anda simpan. URL mock menerima bentuk body yang sama, sehingga frontend dapat melakukan `POST` terhadapnya seolah-olah itu adalah server Rust yang sebenarnya.
Untuk mock dinamis, beralihlah ke Advanced Mock dan tulis skrip:
return {
id: Math.floor(Math.random() * 10000),
name: body.name,
email: body.email,
createdAt: new Date().toISOString()
};
Mock tersebut merespons apa pun yang dikirim frontend, dengan `id` dan stempel waktu yang dihasilkan. Ketika handler Rust sudah siap, frontend mengembalikan URL dasarnya ke `http://localhost:3000` dan tidak ada hal lain yang berubah. Untuk lebih lanjut tentang pola ini, tim juga membahas membangun dan menguji API Spring Boot dan alur kerja pengujian API umum; ide yang sama, runtime yang berbeda.
Langkah 8: Simpan sebagai skenario pengujian CI
Skenario Pengujian Apidog merangkai permintaan dengan variabel bersama dan menjalankannya tanpa kepala (headlessly). Buat skenario:
- `health-check`, pastikan `200`.
- `create-user`, pastikan `200`, tangkap `body.id` ke dalam variabel.
- `create-user-missing-email`, pastikan `422`.
- `me` (dengan pra-permintaan JWT), pastikan `200` dan `id` yang dikembalikan cocok dengan `id` yang ditangkap.
- Permintaan SSE, pastikan stream selesai dalam 5 detik.
Ekspor skenario sebagai JSON, komit ke repo Anda di bawah `tests/apidog/`, dan panggil dari CI:
- name: Jalankan pengujian kontrak API
run: |
cargo build --release
./target/release/myserver &
sleep 2
apidog-cli run tests/apidog/contract.json --env "Rust Local"
Setiap PR yang menyentuh handler sekarang berjalan terhadap biner Rust langsung dengan rangkaian kontrak lengkap. Jika perubahan nama Serde, perubahan kode status, atau penyesuaian validasi JWT merusak bentuk publik, CI akan menangkapnya sebelum tombol merge menjadi hijau.
Langkah 9: Hasilkan OpenAPI dari permintaan yang tersimpan
Ketika set permintaan stabil, buka menu Ekspor Apidog dan pilih OpenAPI 3.1. Anda mendapatkan dokumen spesifikasi yang mencakup setiap permintaan yang disimpan, dengan body yang Anda kirim sebagai contoh. Berikan itu kepada siapa pun yang membuat klien bertipe (TypeScript, Swift, Kotlin, Python) dan mereka mendapatkan kontrak yang sesuai dengan apa yang dikembalikan server Rust Anda hari ini, bukan apa yang seseorang tulis tangan di `.yaml` enam bulan lalu.
Jika Anda ingin spesifikasi diperiksa ke repo Rust Anda, jalankan `apidog-cli export` dari CI dan tulis ke `openapi.json`. `cargo build` berikutnya tidak akan berubah, tetapi setiap konsumen API Anda mendapatkan kebenaran di disk.
FAQ
Apakah Apidog bekerja dengan Axum dan Actix-web? Ya. Apidog berbicara HTTP, bukan Rust. Apa pun yang merespons permintaan (Axum, Actix-web, Rocket, Warp, Poem, Loco) bekerja dengan cara yang sama. Satu-satunya pertimbangan khusus Rust adalah mengikat ke `0.0.0.0` alih-alih `127.0.0.1` untuk pengujian lokal.
Bagaimana cara menguji handler yang panik? Jalankan server Anda dengan `tower-http`'s `CatchPanicLayer` di depan router. Panik akan berubah menjadi `500` dengan body JSON. Buat permintaan Apidog yang memicu jalur panik dan pastikan `500`. Jika Anda tidak membungkus panik, koneksi terputus dan Apidog melaporkan kesalahan jaringan, yang juga merupakan pengujian kontrak yang valid.
Bisakah saya menjalankan Apidog terhadap biner Rust di Docker? Ya. Arahkan `baseUrl` ke port yang diekspos oleh container dan Anda selesai. Jika container berjalan di dalam Docker Compose, berikan runner Apidog Anda jaringan yang sama atau gunakan port yang dipetakan host.
Bagaimana dengan gRPC? Apidog memiliki jenis permintaan gRPC. Impor file `.proto` Anda, pilih layanan dan metode, isi payload permintaan, dan kirim. Pola otentikasi, lingkungan, dan skenario pengujian identik dengan REST.
Apakah skenario pengujian menggantikan `cargo test`? Tidak. Pengujian unit untuk kode Rust Anda tetap di Rust. Apidog menguji permukaan yang berjalan: kontrak HTTP. Kedua lapisan menangkap bug yang berbeda. Pengujian unit menangkap fungsi yang rusak; pengujian Apidog menangkap bentuk respons yang rusak, header CORS yang hilang, atau `400` yang menjadi `422`. Anda membutuhkan keduanya.
Apakah Apidog gratis untuk proyek open-source Rust? Ya. Klien Apidog gratis untuk individu dan tim kecil. Skenario pengujian, mock, dan ekspor OpenAPI adalah bagian dari tingkat gratis. Jika Anda memelihara API Rust publik, Anda dapat mengirimkan file proyek di repo Anda sehingga siapa pun yang mengkloning mendapatkan rangkaian pengujian.
Rangkuman
API Rust membutuhkan siklus umpan balik yang tidak menunggu kompiler. Kumpulan permintaan di Apidog memberi Anda siklus itu: HTTP nyata, asersi nyata, mock nyata untuk frontend, dan skenario CI yang berjalan terhadap biner langsung. Bangun permintaan di atas sekali, dan setiap perubahan di masa mendatang pada handler Axum atau Actix Anda menjadi jalannya pengujian yang terkontrol alih-alih kejutan saat runtime.
Unduh Apidog dan arahkan ke server Rust Anda. Pengaturannya membutuhkan waktu kurang dari sepuluh menit. Keuntungannya adalah kontrak yang Anda kendalikan, terlepas dari `cargo`, dan tim frontend yang berhenti bertanya kapan handler selesai.
