Portal/Notes πŸ“
Rust

Test

Versi Bahasa Indonesia. Baca dalam Bahasa Inggris

Test adalah fungsi Rust yang memverifikasi bahwa kode non-test berfungsi sesuai yang diharapkan. Body fungsi test biasanya melakukan tiga tindakan ini:

  • Menyiapkan data atau state yang diperlukan.
  • Menjalankan kode yang ingin Anda uji.
  • Memastikan bahwa hasilnya sesuai dengan yang Anda harapkan.

Komunitas Rust berpikir tentang test dalam dua kategori utama: unit tests dan integration tests.

Unit Test

Unit test berukuran kecil dan lebih terfokus, menguji satu modul secara terisolasi pada satu waktu, dan dapat menguji interface privat. Tujuan unit test adalah untuk menguji setiap unit kode secara terisolasi dari kode lainnya untuk dengan cepat menemukan di mana kode berfungsi dan tidak berfungsi sesuai harapan.

Konvensinya adalah membuat modul bernama tests di setiap file untuk memuat fungsi test yang dianotasi dengan #[test] dan menganotasi modul dengan cfg(test).

Anotasi #[cfg(test)] pada modul tests memberi tahu Rust untuk mengkompilasi dan menjalankan kode test hanya saat kita menjalankan cargo test, bukan saat kita menjalankan cargo build. Ini menghemat waktu kompilasi dan menghemat ruang di artifact yang dikompilasi saat menjalankan cargo build.

Contoh:

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

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

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}

Jalankan test menggunakan cargo test.

Integration Test

Integration test sepenuhnya eksternal terhadap library Anda dan menggunakan kode Anda dengan cara yang sama seperti kode eksternal lainnya, hanya menggunakan public interface dan berpotensi menggunakan banyak modul per test.

Untuk membuat integration test, pertama-tama Anda perlu direktori tests di level teratas direktori proyek Anda, di samping src. Cargo tahu untuk mencari file integration test di direktori ini. Kita kemudian dapat membuat file test sebanyak yang kita mau, dan Cargo akan mengkompilasi setiap file sebagai crate individual.

adder
β”œβ”€β”€ Cargo.lock
β”œβ”€β”€ Cargo.toml
β”œβ”€β”€ src
β”‚Β Β  └── lib.rs
└── tests
    └── integration_test.rs

Mari tambahkan kode ini ke dalam src/lib.rs:

pub mod adder {
    pub fn add(left: usize, right: usize) -> usize {
        left + right
    }
}

Dan kode integration test ini di test/integration_test.rs:

use rplay::adder;

#[test]
fn it_adds_two() {
    let result = adder::add(2, 2);
    assert_eq!(result, 4);
}

Karena folder proyek saya bernama rplay, kita mengimpornya menggunakan use rplay::adder;. Silakan sesuaikan dengan nama proyek Anda sendiri.

Jalankan test menggunakan cargo test.

Assertion

Secara umum ada 3 jenis assertion yang disediakan oleh Rust yaitu: assert!, assert_eq!, dan assert_ne!. Tetapi kita dapat menggabungkannya dengan macro dan fungsi yang berbeda untuk membuat test case yang lebih baik.

assert!

  • Memeriksa apakah kondisinya true.
  • Gagal jika false.
#[test]
fn test_condition() {
    let value = 42;
    assert!(value > 40);
}

assert_eq!

  • Memeriksa apakah dua nilai yang diberikan sama.
  • Gagal jika tidak sama.
#[test]
fn test_equality() {
    assert_eq!(2 + 2, 4);
}

assert_ne!

  • Memeriksa apakah dua nilai tidak sama.
  • Gagal jika sama.
#[test]
fn test_inequality() {
    assert_ne!(2 * 3, 7);
}

Custom Panic Message

Kita juga dapat memanggil panic! secara eksplisit jika diperlukan untuk membuat test gagal.

#[test]
fn test_fail() {
    panic!("This test will always fail");
}

Selain itu, semua macro assertion memiliki bentuk kedua, di mana pesan panic kustom dapat diberikan dengan atau tanpa argumen untuk formatting.

#[test]
fn test_fail() {
    let x = false;
    assert!(x, "x wasn't true!");
}

Dan jika gagal akan menghasilkan output seperti ini:

---- tests::test_condition stdout ----
thread 'tests::test_condition' panicked at src/main.rs:9:9:
x wasn't true!
stack backtrace:
   0: _rust_begin_unwind
   1: core::panicking::panic_fmt
   2: rplay::tests::test_condition
             at ./src/main.rs:9:9
   3: rplay::tests::test_condition::{{closure}}
             at ./src/main.rs:7:24
   4: core::ops::function::FnOnce::call_once
             at /private/tmp/rust-20250109-8032-r89n27/rustc-1.84.0-src/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Mengharapkan Panic

Kita dapat menggunakan anotasi #[should_panic] jika mengharapkan test akan menghasilkan panic. Misalnya, memeriksa apakah suatu fungsi panic seperti yang diharapkan:

#[test]
#[should_panic]
fn test_divide_by_zero() {
    let _ = 1 / 0;
}

Fungsi yang Sering Dipasangkan untuk Assertions

Gunakan macro dan fungsi mirip assert lainnya yang sering digunakan di Rust untuk memvalidasi kondisi selama pengujian atau debugging.

matches!: Memvalidasi apakah suatu nilai cocok dengan pola tertentu.

#[test]
fn test_matches() {
    enum Result {
        Ok(i32),
        Err(String),
    }

    let value = Result::Ok(10);
    assert!(matches!(value, Result::Ok(_))); // Memeriksa apakah value adalah Result::Ok
}

Validation Functions: .is_empty() / .is_none() / .is_some() / .is_ok() / .is_err()

#[test]
fn test_result() {
    let result: Result<i32, &str> = Ok(10);
    assert!(result.is_ok());
    assert_eq!(result.unwrap(), 10);
}

#[test]
fn test_option() {
    let value: Option<i32> = Some(10);
    assert!(value.is_some());
    assert_eq!(value.unwrap(), 10);
}

Iterator Functions: .any() / .all() / .count()

fn test_positive_numbers() {
    let numbers = vec![1, 2, 3];
    assert!(numbers.iter().all(|&x| x > 0)); // Memastikan semua angka positif
}

Regular Expression: regex::Regex

Install di proyek Anda menggunakan cargo add regex.

use regex::Regex;

#[test]
fn test_regex() {
    let re = Regex::new(r"^\d+$").unwrap();
    assert!(re.is_match("12345"));
}

Best Practices

  • Gunakan assert_eq! atau assert_ne!: Mereka memberikan pesan error yang lebih baik daripada assert! biasa untuk pemeriksaan kesetaraan.
  • Manfaatkan matches! untuk Pattern Matching: Lebih bersih dan lebih ekspresif daripada pencocokan manual.
  • Gunakan Nama Test yang Deskriptif: Jelaskan apa yang sedang diuji dan dalam kondisi apa.
  • Gunakan Custom Panic Messages: Tambahkan pesan yang membantu pada assertion.
  • Jaga Assertions Tetap Fokus: Pastikan setiap test memeriksa satu kondisi atau kondisi kecil yang terkait.

Referensi


Edit on GitHub

Last updated on

Test | Faisal Affan