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:

rust
/// 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:

bash
cargo test

Output yang Dihasilkan:

text
   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.14s

Bagian Doc-tests pajak yang selama ini selalu running 0 tests kini memuat satu test. Mari kita bedah kode dan laporannya:

  1. 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 dihasilkan cargo doc.
  2. 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: rustdoc mengambil blok ini apa adanya, membungkusnya dalam fungsi main, mengompilasi, lalu menjalankannya.
  3. Baris 6 (let pajak = pajak::hitung_pajak(200_000);): pemanggilan memakai prefiks pajak:: — nama crate — karena doc test dikompilasi sebagai pengguna eksternal. Menulis hitung_pajak(200_000) tanpa prefiks akan gagal dikompilasi.
  4. Baris 7 (assert_eq!(pajak, 22_000);): assertion di dalam contoh dievaluasi sungguhan; 11 persen dari 200_000 memang 22_000, contohnya lulus.
  5. Baris 9 (pub fn hitung_pajak(...)): fungsi berikut doc comment-nya wajib pub agar bisa diakses dari posisi pengguna eksternal — syarat yang sama dengan integration test.
  6. Baris laporan test src/lib.rs - hitung_pajak (line 5) ... ok: nama doc test memuat file sumber, nama item, dan line 5 — baris pembuka pagar blok contoh. Angka ini mengikuti posisi blok dalam file.
  7. 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 — bawaan cargo new pada 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:

rust
/// 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:

text
   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:

bash
cargo test --doc

Output yang Dihasilkan:

text
   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.13s

Opsi --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.