পাঠ ১১.৩

Test organize করা

Test Organization

Rust community-তে test দু'ভাগে — unit test এবং integration test

  • Unit test — ছোট, focused; এক module-কে isolation-এ test, private function-ও access করতে পারে।
  • Integration test — library-এর বাইরে; শুধু public API use করে, একাধিক module একসাথে ব্যবহার করতে পারে।

Unit test

src/-এর মধ্যে — যেই file-এ code, সেই file-এই test। Convention — প্রতিটা file-এ একটা tests নামের module, #[cfg(test)] সহ।

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-এ compile কর"। release build-এ থাকবেই না — compile time + binary size save।

Private function test

Rust-এ test code-ও সাধারণ code। tests module child — তাই parent-এর private item access করতে পারে:

src/lib.rsrust
pub fn add_two(a: u64) -> u64 {
    internal_adder(a, 2)
}

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

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

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

internal_adder private হলেও child tests module থেকে access করা যাচ্ছে। use super::* পুরো parent scope নিয়ে আসে।

Integration test

Project root-এ tests/ directory (src/-এর পাশে)। এর প্রতিটা file আলাদা crate

adder
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── tests
    └── integration_test.rs
tests/integration_test.rsrust
use adder::add_two;

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

#[cfg(test)] দরকার নেই — Cargo এই directory special-ভাবে treat করে। আর library-কে use করতে use adder::add_two; লিখতে হয়, কারণ এই file আলাদা crate।

cargo test output — তিন section

     Running unittests src/lib.rs (...)
running 1 test
test tests::internal ... ok

     Running tests/integration_test.rs (...)
running 1 test
test it_adds_two ... ok

   Doc-tests adder
running 0 tests

Unit, integration, doc — তিন section। কোনো section-এ test fail করলে পরের section-গুলো run হয় না।

Specific integration test file

$ cargo test --test integration_test

Submodule integration test-এ

একাধিক integration test file-এর মধ্যে helper share করতে চাই। tests/common.rs বানালে — সেটা একটা আলাদা test crate হয়ে যায়:

cargo test output (unwanted)text
     Running tests/common.rs (...)
running 0 tests

এই empty section ugly। সমাধান — tests/common/mod.rs:

tests
├── common
│   └── mod.rs
└── integration_test.rs

Subdirectory-এর file আলাদা crate হয় না — শুধু helper module।

tests/integration_test.rsrust
use adder::add_two;

mod common;

#[test]
fn it_adds_two() {
    common::setup();

    let result = add_two(2);
    assert_eq!(result, 4);
}

Binary crate-এ integration test?

শুধু src/main.rs থাকলে — integration test সম্ভব না। কারণ binary crate-এর function অন্য crate use করতে পারে না।

সমাধান — convention হলো ছোট src/main.rs যেটা সব logic-কে src/lib.rs-এ delegate করে। তখন integration test library-কে test করে; main.rs-এর সামান্য code-ও indirectly covered।

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

  • Unit test — src/-এ, same file, #[cfg(test)] mod tests; private access possible।
  • Integration test — tests/ directory-তে, প্রতিটা file আলাদা crate; শুধু public API।
  • Three section output — unit, integration, doc।
  • Helper module — tests/common/mod.rs (subdirectory)।
  • Binary-only crate-এ integration test হয় না — main.rs-কে পাতলা রাখো, logic lib.rs-এ।