Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

টেস্ট কিভাবে লিখতে হয়

টেস্ট হলো Rust ফাংশন যা ভেরিফাই (verify) করে যে নন-টেস্ট কোড প্রত্যাশিত পদ্ধতিতে কাজ করছে কিনা। টেস্ট ফাংশনের বডি (body) সাধারণত এই তিনটি কাজ করে:

  • প্রয়োজনীয় ডেটা বা স্টেট সেট আপ করা।
  • আপনি যে কোডটি টেস্ট করতে চান তা রান করা।
  • ফলাফল আপনার প্রত্যাশা অনুযায়ী কিনা তা অ্যাসার্ট (assert) করা।

আসুন, Rust এর সেই ফিচারগুলো দেখি যা বিশেষভাবে টেস্ট লেখার জন্য এই কাজগুলো করতে সাহায্য করে। এর মধ্যে রয়েছে test অ্যাট্রিবিউট, কয়েকটি ম্যাক্রো এবং should_panic অ্যাট্রিবিউট।

একটি টেস্ট ফাংশনের গঠন

সহজ ভাষায়, Rust-এ একটি টেস্ট হলো এমন একটি ফাংশন যা test অ্যাট্রিবিউট দিয়ে অ্যানোটেট (annotated) করা থাকে। অ্যাট্রিবিউট হলো Rust কোডের বিভিন্ন অংশ সম্পর্কে মেটাডেটা; এর একটি উদাহরণ হলো derive অ্যাট্রিবিউট যা আমরা পঞ্চম অধ্যায়ে struct-এর সাথে ব্যবহার করেছি। একটি সাধারণ ফাংশনকে টেস্ট ফাংশনে রূপান্তর করতে, fn এর আগের লাইনে #[test] যোগ করুন। যখন আপনি cargo test কমান্ড দিয়ে আপনার টেস্টগুলো চালান, তখন Rust একটি টেস্ট রানার বাইনারি (test runner binary) তৈরি করে যা এই অ্যানোটেট করা ফাংশনগুলো চালায় এবং প্রতিটি টেস্ট ফাংশন পাস করেছে না ফেইল করেছে তার রিপোর্ট দেয়।

যখনই আমরা Cargo দিয়ে একটি নতুন লাইব্রেরি প্রজেক্ট তৈরি করি, তখন আমাদের জন্য স্বয়ংক্রিয়ভাবে একটি টেস্ট মডিউল এবং তার ভেতরে একটি টেস্ট ফাংশন তৈরি হয়ে যায়। এই মডিউলটি আপনাকে টেস্ট লেখার জন্য একটি টেমপ্লেট দেয়, যাতে প্রতিবার নতুন প্রজেক্ট শুরু করার সময় আপনাকে সঠিক গঠন এবং সিনট্যাক্স খুঁজতে না হয়। আপনি যত খুশি অতিরিক্ত টেস্ট ফাংশন এবং টেস্ট মডিউল যোগ করতে পারেন!

কোনো কোড টেস্ট করার আগে, আমরা টেমপ্লেট টেস্টটি নিয়ে পরীক্ষা করে দেখব টেস্টগুলো কীভাবে কাজ করে। তারপর আমরা কিছু বাস্তবসম্মত টেস্ট লিখব যা আমাদের লেখা কোডকে কল করবে এবং তার আচরণ সঠিক কিনা তা অ্যাসার্ট করবে।

আসুন adder নামে একটি নতুন লাইব্রেরি প্রজেক্ট তৈরি করি যা দুটি সংখ্যা যোগ করবে:

$ cargo new adder --lib
     Created library `adder` project
$ cd adder

আপনার adder লাইব্রেরির src/lib.rs ফাইলের কন্টেন্ট তালিকা ১১-১ এর মতো দেখতে হওয়া উচিত।

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);
    }
}

ফাইলটি একটি উদাহরণ add ফাংশন দিয়ে শুরু হয়, যাতে আমাদের টেস্ট করার জন্য কিছু একটা থাকে।

আপাতত, আসুন আমরা শুধু it_works ফাংশনটির উপর মনোযোগ দিই। #[test] অ্যানোটেশনটি লক্ষ্য করুন: এই অ্যাট্রিবিউটটি নির্দেশ করে যে এটি একটি টেস্ট ফাংশন, তাই টেস্ট রানার জানে যে এই ফাংশনটিকে একটি টেস্ট হিসাবে গণ্য করতে হবে। আমাদের tests মডিউলে নন-টেস্ট ফাংশনও থাকতে পারে যা সাধারণ পরিস্থিতি সেট আপ করতে বা সাধারণ অপারেশন করতে সাহায্য করে, তাই আমাদের সবসময় নির্দিষ্ট করে দিতে হবে কোন ফাংশনগুলো টেস্ট।

উদাহরণ ফাংশন বডি assert_eq! ম্যাক্রো ব্যবহার করে অ্যাসার্ট করে যে result (যেখানে ২ এবং ২ দিয়ে add কল করার ফলাফল রয়েছে) এর মান ৪ এর সমান। এই অ্যাসার্শনটি একটি সাধারণ টেস্টের ফরম্যাটের উদাহরণ হিসাবে কাজ করে। চলুন এটি রান করে দেখি যে এই টেস্টটি পাস করে কিনা।

cargo test কমান্ডটি আমাদের প্রজেক্টের সমস্ত টেস্ট চালায়, যা তালিকা ১১-২-এ দেখানো হয়েছে।

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.57s
     Running unittests src/lib.rs (target/debug/deps/adder-01ad14159ff659ab)

running 1 test
test tests::it_works ... ok

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

   Doc-tests adder

running 0 tests

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

Cargo টেস্টটি কম্পাইল এবং রান করেছে। আমরা running 1 test লাইনটি দেখতে পাচ্ছি। পরবর্তী লাইনটি জেনারেট হওয়া টেস্ট ফাংশনের নাম দেখায়, যা হলো tests::it_works এবং সেই টেস্টটি চালানোর ফলাফল হলো ok। সামগ্রিক সারাংশ test result: ok. এর মানে হলো সমস্ত টেস্ট পাস করেছে এবং 1 passed; 0 failed অংশটি পাস বা ফেইল করা টেস্টের সংখ্যা দেখায়।

একটি টেস্টকে ইগনোর (ignored) হিসেবে চিহ্নিত করা সম্ভব যাতে এটি একটি নির্দিষ্ট ক্ষেত্রে রান না হয়; আমরা এই অধ্যায়ের পরে "Ignoring Some Tests Unless Specifically Requested" বিভাগে এটি আলোচনা করব। যেহেতু আমরা এখানে তা করিনি, তাই সারাংশে 0 ignored দেখাচ্ছে। আমরা cargo test কমান্ডে একটি আর্গুমেন্ট পাস করে শুধুমাত্র সেইসব টেস্ট চালাতে পারি যাদের নাম একটি স্ট্রিং এর সাথে মেলে; একে ফিল্টারিং বলা হয় এবং আমরা এটি "Running a Subset of Tests by Name” বিভাগে আলোচনা করব। এখানে আমরা কোনো টেস্ট ফিল্টার করিনি, তাই সারাংশের শেষে 0 filtered out দেখাচ্ছে।

0 measured পরিসংখ্যানটি বেঞ্চমার্ক টেস্টের জন্য যা পারফরম্যান্স পরিমাপ করে। বেঞ্চমার্ক টেস্ট, এই লেখা পর্যন্ত, শুধুমাত্র নাইটলি রাস্ট-এ (nightly Rust) উপলব্ধ। বেঞ্চমার্ক টেস্ট সম্পর্কে আরও জানতে the documentation about benchmark tests দেখুন।

টেস্ট আউটপুটের পরবর্তী অংশ যা Doc-tests adder দিয়ে শুরু হয়েছে, তা যেকোনো ডকুমেন্টেশন টেস্টের ফলাফলের জন্য। আমাদের এখনো কোনো ডকুমেন্টেশন টেস্ট নেই, কিন্তু Rust আমাদের API ডকুমেন্টেশনে থাকা যেকোনো কোড উদাহরণ কম্পাইল করতে পারে। এই ফিচারটি আপনার ডকুমেন্টেশন এবং আপনার কোডকে সিঙ্কে রাখতে সাহায্য করে! আমরা ১৪ অধ্যায়ের “Documentation Comments as Tests” বিভাগে কীভাবে ডকুমেন্টেশন টেস্ট লিখতে হয় তা আলোচনা করব। আপাতত, আমরা Doc-tests আউটপুটটি উপেক্ষা করব।

আসুন আমাদের প্রয়োজন অনুযায়ী টেস্টটি কাস্টমাইজ করা শুরু করি। প্রথমে, it_works ফাংশনের নাম পরিবর্তন করে অন্য কোনো নাম দিন, যেমন exploration, এইভাবে:

Filename: src/lib.rs

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

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

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

তারপর আবার cargo test চালান। আউটপুট এখন it_works এর পরিবর্তে exploration দেখাবে:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.59s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::exploration ... ok

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

   Doc-tests adder

running 0 tests

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

এখন আমরা আরেকটি টেস্ট যোগ করব, কিন্তু এবার আমরা এমন একটি টেস্ট তৈরি করব যা ফেইল করবে! টেস্ট তখন ফেইল করে যখন টেস্ট ফাংশনের কোনো কিছু প্যানিক (panic) করে। প্রতিটি টেস্ট একটি নতুন থ্রেডে (thread) চালানো হয়, এবং যখন প্রধান থ্রেড দেখে যে একটি টেস্ট থ্রেড মারা গেছে, তখন টেস্টটিকে ফেইল হিসেবে চিহ্নিত করা হয়। নবম অধ্যায়ে আমরা আলোচনা করেছি যে প্যানিক করার সবচেয়ে সহজ উপায় হলো panic! ম্যাক্রো কল করা। নতুন টেস্টটি another নামে একটি ফাংশন হিসাবে প্রবেশ করান, যাতে আপনার src/lib.rs ফাইলটি তালিকা ১১-৩ এর মতো দেখায়।

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

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

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

    #[test]
    fn another() {
        panic!("Make this test fail");
    }
}

cargo test ব্যবহার করে আবার টেস্টগুলো চালান। আউটপুটটি তালিকা ১১-৪ এর মতো হওয়া উচিত, যা দেখায় যে আমাদের exploration টেস্ট পাস করেছে এবং another ফেইল করেছে।

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.72s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 2 tests
test tests::another ... FAILED
test tests::exploration ... ok

failures:

---- tests::another stdout ----

thread 'tests::another' panicked at src/lib.rs:17:9:
Make this test fail
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::another

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

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

ok এর পরিবর্তে, test tests::another লাইনটি FAILED দেখাচ্ছে। স্বতন্ত্র ফলাফলের এবং সারাংশের মধ্যে দুটি নতুন বিভাগ উপস্থিত হয়েছে: প্রথমটি প্রতিটি টেস্ট ফেইলের বিস্তারিত কারণ প্রদর্শন করে। এক্ষেত্রে, আমরা বিস্তারিতভাবে জানতে পারি যে tests::another ফেইল করেছে কারণ এটি src/lib.rs ফাইলের ১৭ নম্বর লাইনে Make this test fail বার্তা দিয়ে প্যানিক করেছে। পরবর্তী বিভাগটি শুধুমাত্র সমস্ত ফেইল করা টেস্টের নাম তালিকাভুক্ত করে, যা অনেক টেস্ট এবং অনেক বিস্তারিত ফেইলিং টেস্ট আউটপুট থাকলে কার্যকর। আমরা একটি ফেইল করা টেস্টের নাম ব্যবহার করে শুধুমাত্র সেই টেস্টটি চালাতে পারি যাতে এটি ডিবাগ করা সহজ হয়; আমরা “Controlling How Tests Are Run” বিভাগে টেস্ট চালানোর উপায় সম্পর্কে আরও কথা বলব।

সারাংশ লাইনটি শেষে প্রদর্শিত হয়: সামগ্রিকভাবে, আমাদের টেস্ট ফলাফল FAILED। আমাদের একটি টেস্ট পাস করেছে এবং একটি ফেইল করেছে।

এখন যেহেতু আপনি বিভিন্ন পরিস্থিতিতে টেস্টের ফলাফল কেমন দেখায় তা দেখেছেন, আসুন panic! ছাড়া অন্য কিছু ম্যাক্রো দেখি যা টেস্টে কার্যকর।

assert! ম্যাক্রো দিয়ে ফলাফল পরীক্ষা করা

assert! ম্যাক্রো, যা স্ট্যান্ডার্ড লাইব্রেরি দ্বারা সরবরাহ করা হয়, তখন খুব দরকারি যখন আপনি নিশ্চিত করতে চান যে টেস্টের কোনো একটি শর্ত true হিসেবে মূল্যায়ন হয়। আমরা assert! ম্যাক্রোকে একটি আর্গুমেন্ট দিই যা একটি বুলিয়ানে (Boolean) পরিণত হয়। যদি মান true হয়, কিছুই হয় না এবং টেস্ট পাস করে। যদি মান false হয়, assert! ম্যাক্রো panic! কল করে টেস্টটিকে ফেইল করায়। assert! ম্যাক্রো ব্যবহার করে আমরা পরীক্ষা করতে পারি যে আমাদের কোডটি আমাদের উদ্দেশ্য অনুযায়ী কাজ করছে কিনা।

অধ্যায় ৫, তালিকা ৫-১৫-তে, আমরা একটি Rectangle struct এবং একটি can_hold মেথড ব্যবহার করেছিলাম, যা এখানে তালিকা ১১-৫-এ পুনরাবৃত্তি করা হলো। চলুন এই কোডটি src/lib.rs ফাইলে রাখি, তারপর assert! ম্যাক্রো ব্যবহার করে এর জন্য কিছু টেস্ট লিখি।

#[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
    }
}

can_hold মেথডটি একটি বুলিয়ান রিটার্ন করে, যার মানে এটি assert! ম্যাক্রোর জন্য একটি উপযুক্ত ব্যবহারক্ষেত্র। তালিকা ১১-৬-এ, আমরা একটি টেস্ট লিখছি যা can_hold মেথডটি ব্যবহার করে। এতে আমরা ৮ প্রস্থ এবং ৭ উচ্চতার একটি Rectangle ইনস্ট্যান্স তৈরি করি এবং অ্যাসার্ট করি যে এটি ৫ প্রস্থ এবং ১ উচ্চতার আরেকটি Rectangle ইনস্ট্যান্সকে ধারণ করতে পারে।

#[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));
    }
}

tests মডিউলের ভিতরে use super::*; লাইনটি লক্ষ্য করুন। tests মডিউলটি একটি সাধারণ মডিউল যা আমরা ৭ অধ্যায়ের "Paths for Referring to an Item in the Module Tree" বিভাগে আলোচনা করা সাধারণ ভিজিবিলিটি নিয়ম অনুসরণ করে। যেহেতু tests মডিউলটি একটি অভ্যন্তরীণ মডিউল, তাই আমাদের বাইরের মডিউলের কোডটি ভেতরের মডিউলের স্কোপে আনতে হবে। আমরা এখানে একটি গ্লব (glob) ব্যবহার করি, তাই বাইরের মডিউলে আমরা যা কিছু ডিফাইন করি তা এই tests মডিউলের জন্য উপলব্ধ থাকে।

আমরা আমাদের টেস্টের নাম দিয়েছি larger_can_hold_smaller এবং আমাদের প্রয়োজনীয় দুটি Rectangle ইনস্ট্যান্স তৈরি করেছি। তারপর আমরা assert! ম্যাক্রো কল করে তাতে larger.can_hold(&smaller) এর ফলাফল পাস করেছি। এই এক্সপ্রেশনটির true রিটার্ন করার কথা, তাই আমাদের টেস্ট পাস করা উচিত। চলুন দেখি!

$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 1 test
test tests::larger_can_hold_smaller ... ok

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

   Doc-tests rectangle

running 0 tests

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

এটি পাস করেছে! আসুন আরেকটি টেস্ট যোগ করি, এবার অ্যাসার্ট করব যে একটি ছোট আয়তক্ষেত্র একটি বড় আয়তক্ষেত্রকে ধারণ করতে পারে না:

Filename: src/lib.rs

#[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() {
        // --snip--
        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));
    }
}

যেহেতু এই ক্ষেত্রে can_hold ফাংশনের সঠিক ফলাফল false, তাই আমাদের assert! ম্যাক্রোতে পাস করার আগে সেই ফলাফলটিকে নেগেট (negate) করতে হবে। ফলস্বরূপ, যদি can_hold false রিটার্ন করে তাহলে আমাদের টেস্ট পাস করবে:

$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 2 tests
test tests::larger_can_hold_smaller ... ok
test tests::smaller_cannot_hold_larger ... ok

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

   Doc-tests rectangle

running 0 tests

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

দুটি টেস্টই পাস করেছে! এখন দেখা যাক আমাদের কোডে একটি বাগ প্রবেশ করালে টেস্টের ফলাফলে কী ঘটে। আমরা can_hold মেথডের ইমপ্লিমেন্টেশনে প্রস্থ তুলনা করার সময় গ্রেটার-দ্যান চিহ্নের পরিবর্তে একটি লেস-দ্যান চিহ্ন দিয়ে পরিবর্তন করব:

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

// --snip--
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));
    }
}

এখন টেস্ট চালালে নিম্নলিখিত ফলাফল পাওয়া যাবে:

$ cargo test
   Compiling rectangle v0.1.0 (file:///projects/rectangle)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/rectangle-6584c4561e48942e)

running 2 tests
test tests::larger_can_hold_smaller ... FAILED
test tests::smaller_cannot_hold_larger ... ok

failures:

---- tests::larger_can_hold_smaller stdout ----

thread 'tests::larger_can_hold_smaller' panicked at src/lib.rs:28:9:
assertion failed: larger.can_hold(&smaller)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::larger_can_hold_smaller

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

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

আমাদের টেস্ট বাগটি ধরে ফেলেছে! যেহেতু `larger.width` হলো `8` এবং `smaller.width` হলো `5`, `can_hold`-এ প্রস্থের তুলনা এখন `false` রিটার্ন করছে: ৮, ৫-এর থেকে ছোট নয়।

### `assert_eq!` এবং `assert_ne!` ম্যাক্রো দিয়ে সমতা পরীক্ষা করা

ফাংশনালিটি যাচাই করার একটি সাধারণ উপায় হলো, টেস্ট করা কোডের ফলাফল এবং আপনার প্রত্যাশিত মানের মধ্যে সমতা পরীক্ষা করা। আপনি `assert!` ম্যাক্রো এবং `==` অপারেটর ব্যবহার করে এটি করতে পারেন। যাইহোক, এটি এত সাধারণ একটি পরীক্ষা যে স্ট্যান্ডার্ড লাইব্রেরি এই কাজটি আরও সুবিধাজনকভাবে করার জন্য `assert_eq!` এবং `assert_ne!`—নামে একজোড়া ম্যাক্রো সরবরাহ করে। এই ম্যাক্রোগুলো দুটি আর্গুমেন্টকে যথাক্রমে সমতা বা অসমতার জন্য তুলনা করে। যদি অ্যাসার্শন ফেইল করে, তবে তারা দুটি মানই প্রিন্ট করবে, যা টেস্টটি _কেন_ ফেইল করেছে তা দেখতে সহজ করে তোলে; বিপরীতভাবে, `assert!` ম্যাক্রো শুধুমাত্র নির্দেশ করে যে এটি `==` এক্সপ্রেশনের জন্য একটি `false` মান পেয়েছে, কিন্তু যে মানগুলোর কারণে `false` হয়েছে তা প্রিন্ট করে না।

তালিকা ১১-৭-এ, আমরা `add_two` নামে একটি ফাংশন লিখছি যা এর প্যারামিটারের সাথে `2` যোগ করে, তারপর আমরা `assert_eq!` ম্যাক্রো ব্যবহার করে এই ফাংশনটি টেস্ট করি।

<Listing number="11-7" file-name="src/lib.rs" caption="`assert_eq!` ম্যাক্রো ব্যবহার করে `add_two` ফাংশন টেস্ট করা">

```rust,noplayground
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);
    }
}

চলুন পরীক্ষা করি এটি পাস করে কিনা!

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_adds_two ... ok

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

   Doc-tests adder

running 0 tests

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

আমরা result নামে একটি ভ্যারিয়েবল তৈরি করেছি যা add_two(2) কল করার ফলাফল ধারণ করে। তারপর আমরা result এবং 4 কে assert_eq! ম্যাক্রোর আর্গুমেন্ট হিসেবে পাস করেছি। এই টেস্টের আউটপুট লাইনটি হলো test tests::it_adds_two ... ok, এবং ok টেক্সটটি নির্দেশ করে যে আমাদের টেস্ট পাস করেছে!

আসুন আমাদের কোডে একটি বাগ প্রবেশ করাই এবং দেখি assert_eq! ফেইল করলে কেমন দেখায়। add_two ফাংশনের ইমপ্লিমেন্টেশন পরিবর্তন করে 3 যোগ করার ব্যবস্থা করি:

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

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

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

আবার টেস্ট চালান:

$ cargo test
   Compiling adder v0.1.0 (file:///projects/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.61s
     Running unittests src/lib.rs (target/debug/deps/adder-92948b65e88960b4)

running 1 test
test tests::it_adds_two ... FAILED

failures:

---- tests::it_adds_two stdout ----

thread 'tests::it_adds_two' panicked at src/lib.rs:12:9:
assertion `left == right` failed
  left: 5
 right: 4
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::it_adds_two

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`

আমাদের টেস্ট বাগটি ধরে ফেলেছে! tests::it_adds_two টেস্টটি ফেইল করেছে, এবং মেসেজটি আমাদের বলছে যে যে অ্যাসার্শনটি ফেইল করেছে তা হলো left == right এবং leftright এর মান কী। এই মেসেজটি আমাদের ডিবাগিং শুরু করতে সাহায্য করে: left আর্গুমেন্ট, যেখানে add_two(2) কল করার ফলাফল ছিল, সেটি ছিল 5 কিন্তু right আর্গুমেন্ট ছিল 4। আপনি কল্পনা করতে পারেন যে যখন আমাদের অনেকগুলো টেস্ট থাকবে তখন এটি বিশেষভাবে সহায়ক হবে।

উল্লেখ্য যে কিছু ভাষা এবং টেস্ট ফ্রেমওয়ার্কে, সমতা অ্যাসার্শন ফাংশনের প্যারামিটারগুলোকে expected এবং actual বলা হয় এবং আমরা কোন ক্রমে আর্গুমেন্টগুলো নির্দিষ্ট করি তা গুরুত্বপূর্ণ। যাইহোক, Rust-এ এগুলোকে left এবং right বলা হয় এবং আমরা প্রত্যাশিত মান এবং কোডের উৎপাদিত মানের ক্রম নির্দিষ্ট করার ক্ষেত্রে কোনো বাধ্যবাধকতা নেই। আমরা এই টেস্টের অ্যাসার্শনটি assert_eq!(4, result) হিসেবেও লিখতে পারতাম, যা একই ফেইলার মেসেজ দিত যা assertion `left == right` failed প্রদর্শন করে।

assert_ne! ম্যাক্রো পাস করবে যদি আমরা দেওয়া দুটি মান সমান না হয় এবং ফেইল করবে যদি তারা সমান হয়। এই ম্যাক্রোটি সেইসব ক্ষেত্রে সবচেয়ে কার্যকর যখন আমরা নিশ্চিত নই যে একটি মান কী হবে, কিন্তু আমরা জানি যে মানটি নিশ্চিতভাবে কী হওয়া উচিত নয়। উদাহরণস্বরূপ, যদি আমরা এমন একটি ফাংশন টেস্ট করি যা তার ইনপুটকে কোনোভাবে পরিবর্তন করার গ্যারান্টি দেয়, কিন্তু ইনপুটটি কোন উপায়ে পরিবর্তিত হবে তা সপ্তাহের কোন দিনে আমরা টেস্ট চালাচ্ছি তার উপর নির্ভর করে, তবে সবচেয়ে ভালো অ্যাসার্শন হতে পারে যে ফাংশনের আউটপুট ইনপুটের সমান নয়।

ভিতরে ভিতরে, assert_eq! এবং assert_ne! ম্যাক্রোগুলো যথাক্রমে == এবং != অপারেটর ব্যবহার করে। যখন অ্যাসার্শন ফেইল করে, তখন এই ম্যাক্রোগুলো ডিবাগ ফরম্যাটিং ব্যবহার করে তাদের আর্গুমেন্ট প্রিন্ট করে, যার মানে হলো তুলনা করা মানগুলোকে অবশ্যই PartialEq এবং Debug ট্রেইট ইমপ্লিমেন্ট করতে হবে। সমস্ত প্রিমিটিভ টাইপ এবং স্ট্যান্ডার্ড লাইব্রেরির বেশিরভাগ টাইপ এই ট্রেইটগুলো ইমপ্লিমেন্ট করে। আপনার নিজের ডিফাইন করা struct এবং enum-এর জন্য, সেই টাইপগুলোর সমতা অ্যাসার্ট করতে আপনাকে PartialEq ইমপ্লিমেন্ট করতে হবে। অ্যাসার্শন ফেইল করলে মানগুলো প্রিন্ট করার জন্য আপনাকে Debug ইমপ্লিমেন্ট করতে হবে। যেহেতু উভয়ই ডিরাইভেবল ট্রেইট (derivable traits), যেমনটি অধ্যায় ৫ এর তালিকা ৫-১২ তে উল্লেখ করা হয়েছে, এটি সাধারণত আপনার struct বা enum ডেফিনিশনে #[derive(PartialEq, Debug)] অ্যানোটেশন যোগ করার মতোই সহজ। এই এবং অন্যান্য ডিরাইভেবল ট্রেইট সম্পর্কে আরও বিস্তারিত জানতে পরিশিষ্ট C, “Derivable Traits,” দেখুন।

কাস্টম ফেইলার মেসেজ যোগ করা

আপনি assert!, assert_eq!, এবং assert_ne! ম্যাক্রোগুলোতে ঐচ্ছিক আর্গুমেন্ট হিসেবে ফেইলার মেসেজের সাথে প্রিন্ট করার জন্য একটি কাস্টম মেসেজও যোগ করতে পারেন। প্রয়োজনীয় আর্গুমেন্টের পরে নির্দিষ্ট করা যেকোনো আর্গুমেন্ট format! ম্যাক্রোতে (অধ্যায় ৮-এর “Concatenation with the + Operator or the format! Macro” বিভাগে আলোচিত) পাস করা হয়, তাই আপনি একটি ফরম্যাট স্ট্রিং পাস করতে পারেন যাতে {} প্লেসহোল্ডার এবং সেই প্লেসহোল্ডারে যাওয়ার জন্য মান থাকে। কাস্টম মেসেজ একটি অ্যাসার্শনের অর্থ নথিভুক্ত করার জন্য কার্যকর; যখন একটি টেস্ট ফেইল করে, তখন কোডের সমস্যাটি কী সে সম্পর্কে আপনার একটি ভালো ধারণা থাকবে।

উদাহরণস্বরূপ, ধরা যাক আমাদের এমন একটি ফাংশন আছে যা নাম ধরে লোকেদের সম্ভাষণ জানায় এবং আমরা টেস্ট করতে চাই যে আমরা ফাংশনে যে নামটি পাস করছি তা আউটপুটে প্রদর্শিত হচ্ছে কিনা:

Filename: src/lib.rs

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"));
    }
}

এই প্রোগ্রামের প্রয়োজনীয়তা এখনও ঠিক হয়নি, এবং আমরা প্রায় নিশ্চিত যে সম্ভাষণের শুরুতে থাকা Hello টেক্সট পরিবর্তন হবে। আমরা সিদ্ধান্ত নিয়েছি যে প্রয়োজনীয়তা পরিবর্তিত হলে আমাদের টেস্ট আপডেট করতে হবে না, তাই greeting ফাংশন থেকে ফিরে আসা মানের সাথে হুবহু সমতা পরীক্ষা করার পরিবর্তে, আমরা কেবল অ্যাসার্ট করব যে আউটপুট ইনপুট প্যারামিটারের টেক্সট ধারণ করে।

এখন এই কোডে একটি বাগ প্রবেশ করাই greeting ফাংশন পরিবর্তন করে name বাদ দিয়ে, যাতে ডিফল্ট টেস্ট ফেইলার কেমন দেখায় তা দেখতে পারি:

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

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

    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(result.contains("Carol"));
    }
}

এই টেস্টটি চালালে নিম্নলিখিত ফলাফল পাওয়া যায়:

$ cargo test
   Compiling greeter v0.1.0 (file:///projects/greeter)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.91s
     Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)

running 1 test
test tests::greeting_contains_name ... FAILED

failures:

---- tests::greeting_contains_name stdout ----

thread 'tests::greeting_contains_name' panicked at src/lib.rs:12:9:
assertion failed: result.contains("Carol")
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::greeting_contains_name

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`

এই ফলাফলটি কেবল নির্দেশ করে যে অ্যাসার্শনটি ফেইল করেছে এবং অ্যাসার্শনটি কোন লাইনে আছে। একটি আরও দরকারী ফেইলার মেসেজ greeting ফাংশন থেকে প্রাপ্ত মানটি প্রিন্ট করত। আসুন আমরা একটি কাস্টম ফেইলার মেসেজ যোগ করি যা একটি ফরম্যাট স্ট্রিং এবং greeting ফাংশন থেকে প্রাপ্ত আসল মান দিয়ে পূরণ করা একটি প্লেসহোল্ডার নিয়ে গঠিত:

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

#[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}`"
        );
    }
}

এখন যখন আমরা টেস্টটি চালাব, আমরা একটি আরও তথ্যপূর্ণ ত্রুটি বার্তা পাব:

$ cargo test
   Compiling greeter v0.1.0 (file:///projects/greeter)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.93s
     Running unittests src/lib.rs (target/debug/deps/greeter-170b942eb5bf5e3a)

running 1 test
test tests::greeting_contains_name ... FAILED

failures:

---- tests::greeting_contains_name stdout ----

thread 'tests::greeting_contains_name' panicked at src/lib.rs:12:9:
Greeting did not contain name, value was `Hello!`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    tests::greeting_contains_name

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`

আমরা টেস্ট আউটপুটে আসলে যে মানটি পেয়েছি তা দেখতে পাচ্ছি, যা আমাদের প্রত্যাশার পরিবর্তে কী ঘটেছে তা ডিবাগ করতে সাহায্য করবে।

should_panic দিয়ে প্যানিক পরীক্ষা করা

রিটার্ন ভ্যালু পরীক্ষা করার পাশাপাশি, আমাদের কোড প্রত্যাশা অনুযায়ী ত্রুটির শর্তগুলো পরিচালনা করছে কিনা তা পরীক্ষা করাও গুরুত্বপূর্ণ। উদাহরণস্বরূপ, Guess টাইপটি বিবেচনা করুন যা আমরা অধ্যায় ৯, তালিকা ৯-১৩-এ তৈরি করেছি। Guess ব্যবহারকারী অন্যান্য কোড এই গ্যারান্টির উপর নির্ভর করে যে Guess ইনস্ট্যান্সগুলিতে শুধুমাত্র ১ থেকে ১০০ এর মধ্যে মান থাকবে। আমরা এমন একটি টেস্ট লিখতে পারি যা নিশ্চিত করে যে সেই সীমার বাইরের কোনো মান দিয়ে একটি Guess ইনস্ট্যান্স তৈরি করার চেষ্টা করলে তা প্যানিক করে।

আমরা আমাদের টেস্ট ফাংশনে should_panic অ্যাট্রিবিউট যোগ করে এটি করি। যদি ফাংশনের ভিতরের কোড প্যানিক করে তবে টেস্টটি পাস করে; যদি ফাংশনের ভিতরের কোড প্যানিক না করে তবে টেস্টটি ফেইল করে।

তালিকা ১১-৮ একটি টেস্ট দেখায় যা পরীক্ষা করে যে Guess::new এর ত্রুটির শর্তগুলো আমাদের প্রত্যাশা অনুযায়ী ঘটে কিনা।

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);
    }
}

আমরা #[should_panic] অ্যাট্রিবিউটটি #[test] অ্যাট্রিবিউটের পরে এবং যে টেস্ট ফাংশনে এটি প্রযোজ্য তার আগে স্থাপন করি। আসুন দেখি এই টেস্টটি পাস করলে ফলাফল কেমন হয়:

$ cargo test
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)

running 1 test
test tests::greater_than_100 - should panic ... ok

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

   Doc-tests guessing_game

running 0 tests

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

বেশ ভালো দেখাচ্ছে! এখন আমাদের কোডে একটি বাগ প্রবেশ করাই new ফাংশনের সেই শর্তটি সরিয়ে দিয়ে যা বলে যে মান ১০০ এর বেশি হলে ফাংশনটি প্যানিক করবে:

pub struct Guess {
    value: i32,
}

// --snip--
impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            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);
    }
}

যখন আমরা তালিকা ১১-৮-এর টেস্টটি চালাই, তখন এটি ফেইল করবে:

$ cargo test
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.62s
     Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)

running 1 test
test tests::greater_than_100 - should panic ... FAILED

failures:

---- tests::greater_than_100 stdout ----
note: test did not panic as expected

failures:
    tests::greater_than_100

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`

এক্ষেত্রে আমরা খুব সহায়ক বার্তা পাই না, কিন্তু যখন আমরা টেস্ট ফাংশনটি দেখি, তখন আমরা দেখতে পাই যে এটি #[should_panic] দিয়ে অ্যানোটেটেড। আমরা যে ফেইলার পেয়েছি তার মানে হল টেস্ট ফাংশনের কোডটি প্যানিক ঘটায়নি।

should_panic ব্যবহার করা টেস্টগুলো অসম্পূর্ণ হতে পারে। একটি should_panic টেস্ট পাস করতে পারত এমনকি যদি টেস্টটি আমাদের প্রত্যাশিত কারণের থেকে ভিন্ন কোনো কারণে প্যানিক করত। should_panic টেস্টগুলোকে আরও সুনির্দিষ্ট করতে, আমরা should_panic অ্যাট্রিবিউটে একটি ঐচ্ছিক expected প্যারামিটার যোগ করতে পারি। টেস্ট হারনেস নিশ্চিত করবে যে ফেইলার মেসেজটিতে প্রদত্ত টেক্সট রয়েছে। উদাহরণস্বরূপ, তালিকা ১১-৯-এ Guess-এর পরিবর্তিত কোডটি বিবেচনা করুন যেখানে new ফাংশনটি মানটি খুব ছোট বা খুব বড় হওয়ার উপর নির্ভর করে বিভিন্ন মেসেজ দিয়ে প্যানিক করে।

pub struct Guess {
    value: i32,
}

// --snip--

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            panic!(
                "Guess value must be greater than or equal to 1, got {value}."
            );
        } else if value > 100 {
            panic!(
                "Guess value must be less than or equal to 100, got {value}."
            );
        }

        Guess { value }
    }
}

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

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

এই টেস্টটি পাস করবে কারণ should_panic অ্যাট্রিবিউটের expected প্যারামিটারে আমরা যে মানটি রেখেছি তা Guess::new ফাংশন যে মেসেজ দিয়ে প্যানিক করে তার একটি সাবস্ট্রিং। আমরা প্রত্যাশিত সম্পূর্ণ প্যানিক মেসেজটিও নির্দিষ্ট করতে পারতাম, যা এক্ষেত্রে Guess value must be less than or equal to 100, got 200 হতো। আপনি কী নির্দিষ্ট করতে চান তা নির্ভর করে প্যানিক মেসেজের কতটা অংশ অনন্য বা ডাইনামিক এবং আপনি আপনার টেস্টকে কতটা সুনির্দিষ্ট করতে চান তার উপর। এক্ষেত্রে, প্যানিক মেসেজের একটি সাবস্ট্রিংই নিশ্চিত করার জন্য যথেষ্ট যে টেস্ট ফাংশনের কোডটি else if value > 100 কেসটি এক্সিকিউট করে।

একটি expected মেসেজসহ should_panic টেস্ট ফেইল করলে কী হয় তা দেখতে, আসুন if value < 1 এবং else if value > 100 ব্লকগুলোর বডি অদলবদল করে আমাদের কোডে আবার একটি বাগ প্রবেশ করাই:

pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 {
            panic!(
                "Guess value must be less than or equal to 100, got {value}."
            );
        } else if value > 100 {
            panic!(
                "Guess value must be greater than or equal to 1, got {value}."
            );
        }

        Guess { value }
    }
}

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

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

এবার যখন আমরা should_panic টেস্টটি চালাই, তখন এটি ফেইল করবে:

$ cargo test
   Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running unittests src/lib.rs (target/debug/deps/guessing_game-57d70c3acb738f4d)

running 1 test
test tests::greater_than_100 - should panic ... FAILED

failures:

---- tests::greater_than_100 stdout ----

thread 'tests::greater_than_100' panicked at src/lib.rs:12:13:
Guess value must be greater than or equal to 1, got 200.
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: panic did not contain expected string
      panic message: `"Guess value must be greater than or equal to 1, got 200."`,
 expected substring: `"less than or equal to 100"`

failures:
    tests::greater_than_100

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`

ফেইলার মেসেজটি নির্দেশ করে যে এই টেস্টটি আমাদের প্রত্যাশা অনুযায়ী প্যানিক করেছিল, কিন্তু প্যানিক মেসেজটিতে প্রত্যাশিত স্ট্রিং less than or equal to 100 অন্তর্ভুক্ত ছিল না। এক্ষেত্রে আমরা যে প্যানিক মেসেজটি পেয়েছি তা হল Guess value must be greater than or equal to 1, got 200. এখন আমরা আমাদের বাগ কোথায় তা খুঁজে বের করা শুরু করতে পারি!

টেস্টে Result<T, E> ব্যবহার করা

আমাদের এ পর্যন্ত সব টেস্ট ফেইল করলে প্যানিক করে। আমরা এমন টেস্টও লিখতে পারি যা Result<T, E> ব্যবহার করে! এখানে তালিকা ১১-১ থেকে টেস্টটি Result<T, E> ব্যবহার করে এবং প্যানিক করার পরিবর্তে একটি Err রিটার্ন করার জন্য পুনরায় লেখা হয়েছে:

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"))
        }
    }
}

it_works ফাংশনটির এখন Result<(), String> রিটার্ন টাইপ রয়েছে। ফাংশনের বডিতে, assert_eq! ম্যাক্রো কল করার পরিবর্তে, আমরা টেস্ট পাস করলে Ok(()) এবং টেস্ট ফেইল করলে একটি String সহ Err রিটার্ন করি।

টেস্ট এমনভাবে লেখা যাতে তারা একটি Result<T, E> রিটার্ন করে, তা আপনাকে টেস্টের বডিতে প্রশ্নবোধক চিহ্ন অপারেটর (?) ব্যবহার করতে সক্ষম করে, যা এমন টেস্ট লেখার জন্য একটি সুবিধাজনক উপায় হতে পারে যা তাদের মধ্যে কোনো অপারেশন Err ভ্যারিয়েন্ট রিটার্ন করলে ফেইল করা উচিত।

Result<T, E> ব্যবহার করা টেস্টে আপনি #[should_panic] অ্যানোটেশন ব্যবহার করতে পারবেন না। কোনো অপারেশন একটি Err ভ্যারিয়েন্ট রিটার্ন করে তা অ্যাসার্ট করতে, Result<T, E> মানের উপর প্রশ্নবোধক চিহ্ন অপারেটর ব্যবহার করবেন না। পরিবর্তে, assert!(value.is_err()) ব্যবহার করুন।

এখন যেহেতু আপনি টেস্ট লেখার বিভিন্ন উপায় জানেন, আসুন দেখি আমাদের টেস্ট চালানোর সময় কী ঘটছে এবং cargo test এর সাথে আমরা যে বিভিন্ন অপশন ব্যবহার করতে পারি তা অন্বেষণ করি।