পাঠ ১১.১

Test কীভাবে লিখব

How to Write Tests

Test হলো এমন function যা আমাদের non-test code-এর behavior verify করে। সাধারণ structure — (১) data বা state setup, (২) যা test করতে চাও সেটা run, (৩) result expected কিনা assert।

#[test] attribute

Test হলো #[test] দিয়ে annotated function। cargo test চালালে Rust একটা test runner binary build করে এবং সেগুলো run করে।

terminalbash
$ cargo new adder --lib

Auto-generated src/lib.rs:

src/lib.rsrust
pub fn add(left: u64, right: u64) -> u64 {
    left + right
}

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

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

#[cfg(test)] module-কে শুধু test build-এ include করে — release-এ bake হয় না। Test module-এর ভিতরে non-test helper function-ও থাকতে পারে; #[test] attached না থাকলে runner সেগুলো test হিসেবে চালায় না।

cargo test

terminalbash
$ cargo test
running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Test fail (panic) হলে output-এ ঐ test-এর details আলাদা section-এ:

failures:
---- tests::another stdout ----
thread 'tests::another' panicked at src/lib.rs:17:9:
Make this test fail

assert! macro

assert!(condition) — condition true হলে কিছু না, false হলে panic + test fail।

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

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

    #[test]
    fn larger_can_hold_smaller() {
        let larger = Rectangle { width: 8, height: 7 };
        let smaller = Rectangle { width: 5, height: 1 };

        assert!(larger.can_hold(&smaller));
    }

    #[test]
    fn smaller_cannot_hold_larger() {
        let larger = Rectangle { width: 8, height: 7 };
        let smaller = Rectangle { width: 5, height: 1 };

        assert!(!smaller.can_hold(&larger));
    }
}

assert_eq! এবং assert_ne!

সরাসরি equality test — fail হলে দু'টো value print করে দেয়, debug সহজ:

pub fn add_two(a: u64) -> u64 {
    a + 2
}

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

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

Wrong value হলে output:

assertion `left == right` failed
  left: 5
 right: 4

Important: compared types-এ PartialEq এবং Debug trait দরকার। Custom struct/enum-এ derive করো:

#[derive(PartialEq, Debug)]
struct MyStruct {
    field: i32,
}

Custom failure message

assert/assert_eq/assert_ne — required arg-এর পরে format-style extra arg:

pub fn greeting(name: &str) -> String {
    format!("Hello {name}!")
}

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

    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(
            result.contains("Carol"),
            "Greeting did not contain name, value was `{result}`"
        );
    }
}

should_panic — panic check

কোনো code panic করার কথা — তাহলেই test pass:

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 || value > 100 {
            panic!("Guess value must be between 1 and 100, got {value}.");
        }
        Guess { value }
    }
}

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

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}

Panic-এর message-এ specific text থাকতে হবে — তখন expected argument:

#[test]
#[should_panic(expected = "less than or equal to 100")]
fn greater_than_100() {
    Guess::new(200);
}

এই precision-এ panic ঠিক জায়গা থেকেই হচ্ছে — ভুল reason-এ panic করলে test fail।

Result<T, E> return-করা test

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

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

    #[test]
    fn it_works() -> Result<(), String> {
        let result = add(2, 2);

        if result == 4 {
            Ok(())
        } else {
            Err(String::from("two plus two does not equal four"))
        }
    }
}

Test body-তে ? operator use করতে চাইলে এই form। Err return হলে test fail।

Note: Result-returning test-এ #[should_panic] use করা যায় না। Err হবে কিনা চাইলে — body-তে assert!(value.is_err())

এই পাঠ থেকে যা শিখলে

  • #[test] + #[cfg(test)] mod tests convention; cargo test
  • assert!, assert_eq!, assert_ne! — fail-এ panic; assert_eq/ne-এ PartialEq + Debug দরকার।
  • Custom failure message — extra format arg।
  • #[should_panic(expected = "...")] — specific panic test।
  • Test Result return করতে পারে — body-তে ?; should_panic-এর সাথে mix না।