Dokumentasi yang memuat contoh kode adalah bagian yang paling sering dibaca pengguna — dan paling cepat membusuk. Fungsi berganti nama, tanda tangan berubah, dan contoh di doc comment yang semula benar perlahan menjadi kebohongan manis yang menyesatkan setiap pembaca berikutnya. Rust menyelesaikan masalah ini dengan langkah yang khas: contoh kode di doc comment tidak diperlakukan sebagai teks mati, melainkan sebagai test sungguhan yang dikompilasi dan dijalankan oleh cargo test. Satu sumber kebenaran, dua manfaat — dokumentasi selalu terbarui, dan test-nya lahir gratis dari tulisan kita. Pada laporan cargo test di Chapter-Chapter sebelumnya kita berulang kali melihat bagian Doc-tests yang tertulis running 0 tests; Chapter penutup section ini akhirnya mengisinya. Kita akan menulis doc comment untuk fungsi pajak, membiarkan contohnya diuji, lalu melihat apa yang terjadi ketika contoh dokumentasi berbohong.
Mekanisme: rustdoc Mengekstrak dan Menjalankan Contoh #
Prosesnya berjalan seperti ini. Saat cargo test dijalankan, alat rustdoc memindai seluruh doc comment /// di crate kita dan mengambil setiap blok kode berpagar ``` di dalamnya. Setiap blok kemudian dibungkus menjadi program kecil utuh — konten blok menjadi isi main — lalu dikompilasi dengan tautan ke crate kita dan dijalankan. Contoh di doc comment diperlakukan seperti kode pengguna eksternal: contoh harus memanggil item lewat nama crate, dan hanya item pub yang bisa diakses — posisi yang sama dengan integration test pada Chapter sebelumnya.
Pagarnya pun boleh diberi penanda bahasa ```rust untuk kejelasan, meskipun blok tanpa penanda di doc comment juga dianggap kode Rust secara bawaan. Bila contoh berjalan tanpa panic, test tersebut dilaporkan ok; assertion yang gagal di dalamnya membuat test gagal seperti assertion di unit test biasa.
Doc Test untuk hitung_pajak #
Buat proyek dengan cargo new pajak --lib, lalu isi src/lib.rs berikut:
/// Menghitung besar PPN 11 persen dari sebuah harga.
///
/// # Contoh
///
/// ```
/// let pajak = pajak::hitung_pajak(200_000);
/// assert_eq!(pajak, 22_000);
/// ```
pub fn hitung_pajak(harga: u64) -> u64 {
harga * 11 / 100
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pajak_untuk_harga_nol() {
assert_eq!(hitung_pajak(0), 0);
}
}Jalankan test:
cargo testOutput yang Dihasilkan:
Compiling pajak v0.1.0 (/tmp/w4e-doc-test/pajak)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.14s
Running unittests src/lib.rs (target/debug/deps/pajak-ca9262378e0c3a18)
running 1 test
test tests::pajak_untuk_harga_nol ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests pajak
running 1 test
test src/lib.rs - hitung_pajak (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
all doctests ran in 0.14s; merged doctests compilation took 0.14sBagian Doc-tests pajak yang selama ini selalu running 0 tests kini memuat satu test. Mari kita bedah kode dan laporannya:
- Baris 1-4 (
/// Menghitung ... # Contoh): doc comment dibuka dengan deskripsi fungsi, lalu heading# Contoh— penanda bagian yang dikenal rustdoc dan ditampilkan dengan judul tersendiri di dokumentasi yang dihasilkancargo doc. - Baris 5-8 (blok kode berpagar di dalam doc comment): tiga baris kode
///di dalam pagar ``` adalah contoh yang akan diuji. Inilah doc test-nya:rustdocmengambil blok ini apa adanya, membungkusnya dalam fungsimain, mengompilasi, lalu menjalankannya. - Baris 6 (
let pajak = pajak::hitung_pajak(200_000);): pemanggilan memakai prefikspajak::— nama crate — karena doc test dikompilasi sebagai pengguna eksternal. Menulishitung_pajak(200_000)tanpa prefiks akan gagal dikompilasi. - Baris 7 (
assert_eq!(pajak, 22_000);): assertion di dalam contoh dievaluasi sungguhan; 11 persen dari200_000memang22_000, contohnya lulus. - Baris 9 (
pub fn hitung_pajak(...)): fungsi berikut doc comment-nya wajibpubagar bisa diakses dari posisi pengguna eksternal — syarat yang sama dengan integration test. - Baris laporan
test src/lib.rs - hitung_pajak (line 5) ... ok: nama doc test memuat file sumber, nama item, danline 5— baris pembuka pagar blok contoh. Angka ini mengikuti posisi blok dalam file. - Baris laporan
all doctests ran in 0.14s; merged doctests compilation took 0.14s: durasi eksekusi dan kompilasi doc test; angkannya bervariasi antar mesin. Baris ini tercetak pada crate dengan edition 2024 — bawaancargo newpada cargo versi baru; pembaca yang memakai edition lama atau cargo lama tidak akan melihatnya, dan tidak ada informasi penting yang hilang dari absennya baris tersebut.
Saat Contoh Dokumentasi Berbohong #
Sekarang kita ubah angka pada contoh menjadi salah — dokumentasi mengklaim PPN menghasilkan 23_000, kenyataannya 22_000:
/// Menghitung besar PPN 11 persen dari sebuah harga.
///
/// # Contoh
///
/// ```
/// let pajak = pajak::hitung_pajak(200_000);
/// assert_eq!(pajak, 23_000);
/// ```
pub fn hitung_pajak(harga: u64) -> u64 {
harga * 11 / 100
}Output yang Dihasilkan:
Compiling pajak v0.1.0 (/tmp/w4e-doc-test/pajak)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.07s
Running unittests src/lib.rs (target/debug/deps/pajak-ca9262378e0c3a18)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests pajak
running 1 test
test src/lib.rs - hitung_pajak (line 5) ... FAILED
failures:
---- src/lib.rs - hitung_pajak (line 5) stdout ----
Test executable failed (exit status: 101).
stderr:
thread 'main' (17577) panicked at /tmp/rustdoctestvzWNs4/doctest_bundle_2024.rs:7:1:
assertion `left == right` failed
left: 22000
right: 23000
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
src/lib.rs - hitung_pajak (line 5)
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
all doctests ran in 0.14s; merged doctests compilation took 0.13s
error: doctest failed, to rerun pass `--doc`Beberapa hal menarik dari laporan kegagalan ini. Baris Test executable failed (exit status: 101) menegaskan bahwa contoh dijalankan sebagai program terpisah dan berhenti dengan kode keluar 101 — kode yang sama dengan program Rust yang mengalami panic tak tertangani. Assertion-nya sendiri gagal dalam bentuk yang sudah kita kenal: left: 22000 (hasil aktual fungsi) lawan right: 23000 (angka bohong di dokumentasi). Lokasi panicked at /tmp/rustdoctestvzWNs4/doctest_bundle_2024.rs:7:1 merujuk ke file sementara yang dibuat rustdoc untuk membungkus contoh — nama foldernya acak dan berbeda di setiap eksekusi, jadi angka ini tidak bisa dicocokkan ke file proyek kita. Perbaikannya satu arah: kembalikan contoh menjadi 22_000 agar dokumentasi berhenti berbohong — persis siklus merah-hijau Chapter pertama, kini bekerja pada teks dokumentasi.
Menjalankan Hanya Doc Test #
Saat menulis dokumentasi, kita bisa menjalankan doc test saja tanpa menunggu suite lain:
cargo test --docOutput yang Dihasilkan:
Compiling pajak v0.1.0 (/tmp/w4e-doc-test/pajak)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.02s
Doc-tests pajak
running 1 test
test src/lib.rs - hitung_pajak (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
all doctests ran in 0.14s; merged doctests compilation took 0.13sOpsi --doc adalah pelengkap dari --lib dan --test <nama> yang kita kenal di Chapter Integration Test — tiga penyaring untuk tiga lapisan test dalam satu perintah cargo test.
Ringkasan #
Blok kode berpagar di dalam doc comment /// menjadi doc test: rustdoc membungkus tiap blok dalam program kecil, mengompilasi sebagai pengguna eksternal crate, dan menjalankannya saat cargo test. Laporan bagian Doc-tests menyebut tiap test dengan nama file, item, dan baris pagarnya; contoh bohong gagal dengan laporan left/right dan kode keluar 101. Opsi cargo test --doc menjalankan hanya lapisan ini. Dengan doc test, dokumentasi resmi menjadi bagian dari rangkaian test — penutup yang tepat untuk section ini, karena menulis contoh yang baik kini sama artinya dengan menulis test yang baik.