টেস্ট কিভাবে লিখতে হয়
টেস্ট হলো 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
এবং left
ও right
এর মান কী। এই মেসেজটি আমাদের ডিবাগিং শুরু করতে সাহায্য করে: 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
এর সাথে আমরা যে বিভিন্ন অপশন ব্যবহার করতে পারি তা অন্বেষণ করি।