Dua Chapter pertama section ini sudah tiga kali kita menulis assert_eq! tanpa membahasnya secara khusus. Saatnya turun ke fondasi seluruh assertion di Rust: macro assert!. Macro ini menerima satu kondisi bertipe bool dan menyatakan “kondisi ini harus benar; jika salah, hentikan jalannya program dengan panic”. Pola ini bekerja di dalam test karena, sebagaimana dibahas pada Chapter Filosofi, satu-satunya cara sebuah test dinyatakan gagal adalah mengalami panic. Selain dipakai di test, assert! juga sah dipakai di kode produksi untuk menyatakan kondisi yang tidak boleh mungkin dilanggar, namun fokus kita di sini adalah perannya menyatakan ekspektasi test. Kita akan menguji fungsi login sederhana, membiarkan satu test gagal dengan sengaja, lalu membaca laporan kegagalannya baris demi baris.

Cara Kerja assert! di Balik Layar #

Ketika kompilator menemui assert!(kondisi), macro tersebut mengembang menjadi kode pemeriksa if sederhana: jika kondisi bernilai true, eksekusi berlanjut ke statement berikutnya seolah tidak terjadi apa-apa. Jika kondisi bernilai false, macro memanggil panic! — lengkap dengan lokasi file, baris, dan kolom tempat assert! ditulis.

Dua detail penting mengikuti mekanisme tersebut. Pertama, assert! membutuhkan tipe bool secara ketat; angka, string, atau hasil fungsi non-bool akan ditolak kompilator dengan galat tipe, bukan diam-diam dianggap benar seperti kebiasaan beberapa bahasa lain. Kedua, saat pemanggilan assert! tanpa pesan tambahan gagal, macro mencetak teks sumber kondisi apa adanya — Rust menyimpan representasi teks dari ekspresi yang kita tulis — sehingga pesan galatnya berbunyi assertion failed: <kondisi>. Detail kedua inilah yang membuat pesan kegagalan assert! tetap informatif meski kita tidak menulis pesan apa pun.

Menguji Fungsi Login dengan assert! #

Buat proyek crate pustaka baru bernama akun, lalu isi src/lib.rs dengan fungsi pemeriksa login beserta test-nya:

rust
pub fn cek_login(nama_pengguna: &str, kata_sandi: &str) -> bool {
    nama_pengguna == "budi" && kata_sandi == "rahasia123"
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn login_berhasil() {
        assert!(cek_login("budi", "rahasia123"));
    }

    #[test]
    fn login_gagal_kata_sandi_salah() {
        assert!(!cek_login("budi", "kata-sandi-salah"));
    }

    #[test]
    fn login_gagal_nama_pengguna_salah() {
        assert!(!cek_login("siti", "rahasia123"));
    }
}

Jalankan test:

bash
cargo test

Output yang Dihasilkan:

text
   Compiling akun v0.1.0 (/tmp/w4e-assert-macro/akun)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s
     Running unittests src/lib.rs (target/debug/deps/akun-514e0f9f33bc2811)

running 3 tests
test tests::login_gagal_kata_sandi_salah ... ok
test tests::login_berhasil ... ok
test tests::login_gagal_nama_pengguna_salah ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests akun

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Mari kita bedah kode di atas:

  1. Baris 1 (pub fn cek_login(nama_pengguna: &str, kata_sandi: &str) -> bool): fungsi produksi yang membandingkan kredensial dengan data pengguna contoh. Tipe nilai kembalian bool pas untuk diuji assert! karena macro ini meminta kondisi bertipe bool.
  2. Baris 10 (assert!(cek_login("budi", "rahasia123"));): menyatakan ekspektasi bahwa login dengan kredensial benar harus berhasil. Nilai kembalian true membuat assertion lolos tanpa jejak.
  3. Baris 15 (assert!(!cek_login("budi", "kata-sandi-salah"));): ekspektasi kebalikannya — login dengan kata sandi salah harus gagal, sehingga kita menegaskan hasil negasinya !cek_login(...). Operator ! membalik bool, bukan menegasi makna harfiah, jadi nilai false dari fungsi menjadi true bagi assertion.
  4. Baris 20 (assert!(!cek_login("siti", "rahasia123"));): kasus ketiga, nama pengguna salah dengan kata sandi benar, juga ditegaskan sebagai gagalan login. Tiga test menutup tiga jalur perilaku fungsi: kredensial penuh benar, kata sandi salah, nama pengguna salah.

Saat Test Gagal: Membaca Pesannya #

Sekarang kita sengaja membuat test yang gagal untuk melihat wujud laporannya. Bayangkan kita salah menulis test — test mengira kombinasi ("budi", "kata-sandi-salah") harus berhasil login:

rust
pub fn cek_login(nama_pengguna: &str, kata_sandi: &str) -> bool {
    nama_pengguna == "budi" && kata_sandi == "rahasia123"
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn login_harus_berhasil() {
        assert!(cek_login("budi", "kata-sandi-salah"));
    }
}

Output yang Dihasilkan:

text
   Compiling akun v0.1.0 (/tmp/w4e-assert-macro/akun)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.08s
     Running unittests src/lib.rs (target/debug/deps/akun-514e0f9f33bc2811)

running 1 test
test tests::login_harus_berhasil ... FAILED

failures:

---- tests::login_harus_berhasil stdout ----

thread 'tests::login_harus_berhasil' (19115) panicked at src/lib.rs:11:9:
assertion failed: cek_login("budi", "kata-sandi-salah")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::login_harus_berhasil

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

Baris kunci pada laporan adalah assertion failed: cek_login("budi", "kata-sandi-salah"). Teks setelah titik dua adalah salinan persis kondisi yang kita tulis pada Baris 11, kolom 9 — lokasi assert! yang memicu panic, tercantum pada baris panicked at src/lib.rs:11:9. Angka (19115) di sebelah nama thread adalah ID thread dari sistem operasi yang berbeda pada setiap eksekusi. Dari pesan ini kita bisa langsung menyimpulkan apa yang salah: kondisi bernilai false, artinya cek_login menolak kombinasi kredensial tersebut — test-nya yang keliru mengharapkan, bukan fungsinya yang rusak.

Pesan Galat Kustom dengan Format Argumen #

Pesan bawaan assertion failed: <kondisi> cukup untuk kondisi pendek, namun sering kali kita ingin pesan kegagalan yang menjelaskan konteks. assert! menerima argumen tambahan yang formatnya sama persis dengan println! dan format!: string berisi placeholder {} diikuti nilai-nilai pengisinya. Berikut src/lib.rs lengkap dengan satu test yang memakai pesan kustom:

rust
pub fn cek_login(nama_pengguna: &str, kata_sandi: &str) -> bool {
    nama_pengguna == "budi" && kata_sandi == "rahasia123"
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn login_harus_berhasil() {
        assert!(
            cek_login("budi", "kata-sandi-salah"),
            "login dengan nama pengguna 'budi' seharusnya berhasil, kata sandi yang dikirim: {}",
            "kata-sandi-salah"
        );
    }
}

Output yang Dihasilkan:

text
   Compiling akun v0.1.0 (/tmp/w4e-assert-macro/akun)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.09s
     Running unittests src/lib.rs (target/debug/deps/akun-514e0f9f33bc2811)

running 1 test
test tests::login_harus_berhasil ... FAILED

failures:

---- tests::login_harus_berhasil stdout ----

thread 'tests::login_harus_berhasil' (15620) panicked at src/lib.rs:11:9:
login dengan nama pengguna 'budi' seharusnya berhasil, kata sandi yang dikirim: kata-sandi-salah
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::login_harus_berhasil

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--lib`

Perbedaannya tampak jelas: pesan panic kini berisi kalimat kita sendiri, dengan placeholder {} telah terisi "kata-sandi-salah". Mari kita bedah tiga bagian dari pemanggilan assert! ini:

  1. Baris 12 (cek_login("budi", "kata-sandi-salah"),): argumen pertama tetap kondisi bool — tidak berubah dari bentuk dasar.
  2. Baris 13 ("login dengan nama pengguna ... {}",): argumen kedua adalah format string dengan satu placeholder {}, sama seperti yang kita kenal di println!.
  3. Baris 14 ("kata-sandi-salah"): nilai pengisi placeholder. Karena pemanggilan assert! direntangkan tiga baris, lokasi panic tetap tercatat pada Baris 11 — baris tempat pemanggilan macro dimulai.

Perhatikan pula bahwa pesan kustom menggantikan teks assertion failed: <kondisi> sepenuhnya — kondisi yang gagal tidak lagi dicetak, jadi pastikan pesan kustom memuat informasi yang cukup untuk men-debug. Nilai yang dicetak hanya dievaluasi saat assertion gagal, sehingga menaruh nilai mahal di argumen pesan tidak membebani test yang lulus.

Kondisi Wajib Bertipe bool #

Terakhir, mari kita lihat apa yang terjadi bila kondisi yang diberikan bukan bool. Test berikut mengirim hasil u64 langsung ke assert!:

rust
pub fn sisa_poin(poin: u64) -> u64 {
    poin
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn poin_masih_cukup() {
        assert!(sisa_poin(500));
    }
}

Output yang Dihasilkan:

text
   Compiling akun v0.1.0 (/tmp/w4e-assert-macro/akun)
error[E0308]: mismatched types
  --> src/lib.rs:11:9
   |
11 |         assert!(sisa_poin(500));
   |         ^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `u64`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `akun` (lib test) due to 1 previous error

Galat E0308 ini tertangkap pada saat kompilasi, sebelum satu test pun berjalan. Perbaikannya adalah menuliskan perbandingan eksplisit, misalnya assert!(sisa_poin(500) >= 100);. Rust menolak konversi implisit dari angka ke bool — keputusan yang membuat ekspektasi test selalu tampil gamblang: kita selalu melihat operasi pembanding mana yang diharapkan benar.

Ringkasan #

assert! mengevaluasi satu kondisi bool: nilai true melanjutkan eksekusi, nilai false memicu panic yang menjadikan test gagal. Tanpa pesan tambahan, galatnya berbunyi assertion failed: <kondisi>; dengan pesan kustom berformat println!, galatnya menjadi kalimat kita sendiri dan menggantikan pesan bawaan. Kondisi non-bool ditolak kompilator lewat E0308 sehingga ekspektasi selalu eksplisit. Untuk membandingkan dua nilai secara langsung — pola yang kita tulis berulang kali sebagai assert!(hasil == harapan) — Rust menyediakan bentuk yang lebih kaya informasinya: assert_eq!, pembahasan Chapter berikutnya.