RSpec-এ মক কী?
(বা সাধারণভাবে মক, কারণ এটি RSpec-এ অনন্য কনসেপ্ট নয়।)
একটি মক হলো টেস্টিংয়ের জন্য ব্যবহৃত একটি অবজেক্ট।
আপনি মক ব্যবহার করেন দুটি অবজেক্টের মধ্যে ইন্টারঅ্যাকশন টেস্ট করার জন্য। একটি রেগুলার এক্সপেক্টেশনের মত আউটপুট ভ্যালু টেস্ট করার বদলে।
উদাহরণস্বরূপ:
আপনি একটি API লিখছেন যা ইমেজ ফ্লিপ করে।
আপনি নিজের ইমেজ ম্যানিপুলেশন কোড না লিখে mini_magick এর মত একটি জেম ব্যবহার করেন।
আপনি আপনার কোড এবং এই এক্সটার্নাল ডিপেন্ডেন্সি-র মধ্যে ইন্টারঅ্যাকশন টেস্ট করতে চান… তাই আপনি একটি মক লিখেন যা এক্সপেক্ট করে ImageProcessor ক্লাসে সঠিক মেথডগুলো কল হবে।
এর মানে আপনি প্রতি বার টেস্ট রান করলে ইমেজ ফ্লিপ করবেন না (একটি ধীর অপারেশন)।
এটি কীভাবে কাজ করে?
মকগুলো অরিজিনাল অবজেক্টকে রিপ্লেস করে, তাই আসল মেথডগুলো কল হবে না।
কোড উদাহরণের সময়!
RSpec Mock উদাহরণ
এখানে ImageFlipper টেস্ট:
RSpec.describe "ImageFlipper" do it "calls the flip method with the correct arguments" do mock = double("mini_magick") expect(mock).to receive(:flip).with("ruby.jpg") img = ImageFlipper.new(mock) img.flip("ruby.jpg") end end
এই টেস্ট দিয়ে আমরা TDD ব্যবহার করে আমাদের কোড লিখতে পারি।
প্রথমে:
আমাদের একটি ImageFlipper ক্লাস লিখতে হবে।
এভাবেই:
class ImageFlipper def initialize(image_processor) @image_processor = image_processor end end
আমাদের একটি flip মেথডও দরকার:
def flip(file_name) end
এখন আমরা RSpec থেকে এই ফিডব্যাক পাই:
Failures: 1) ImageFlipper calls the flip method with the correct arguments Failure/Error: expect(mock).to receive(:flip).with("ruby.jpg") (Double "mini_magick").flip("ruby.jpg") expected: 1 time with arguments: ("ruby.jpg") received: 0 times # ./rspec-mocks.rb:6:in `block (2 levels) in <top (required)>'
এটি বলে যে flip মেথডটি ০ বার কল হয়েছে, কিন্তু ১ বার কল হোনোর আশা করা হয়েছিল।
আপনি এটিকে যা চায় সেটি দিয়ে পাস করতে পারেন:
def flip(file_name) @image_processor.flip(file_name) end
এবং ঠিক তাই, আমাদের একটি পাসিং টেস্ট পেয়ে গেলাম:
. Finished in 0.00751 seconds (files took 0.11157 seconds to load) 1 example, 0 failures
রিভিউ করি: আমরা এখানে কী করেছি?
আমরা একটি ImageFlipper ক্লাস তৈরি করেছি যা একটি image_processor নেয়।
এই প্রসেসর flip মেথডে রেসপন্ড করে।
আমরা একটি মক ব্যবহার করেছি টেস্ট করার জন্য যে এই মেথডটি একবার কল হয়েছে, একটি আর্গুমেন্টের সাথে।
আমি জানি।
এই উদাহরণটি সহজ।
কিন্তু আপনি কল্পনা করতে পারেন ImageFlipper-এর একটি সম্পূর্ণ ইমপ্লিমেন্টেশন যা চেক করে ফাইল একজিস্ট করে কিনা, এটি বৈধ ইমেজ কিনা, ইত্যাদি।
মক এবং ভ্যালু টেস্টিং-এর পার্থক্য
একটি রেগুলার টেস্টে আপনি একটি মেথডের রিটার্ন ভ্যালু চেক করেন:
“এই মেথডটি কি একটি ফ্লিপড ইমেজ রিটার্ন করল?”
যখন মক ব্যবহার করেন আপনি আচরণ টেস্ট করেন:
“আমরা কি অন্যদের সঠিক কাজ করতে বললাম, সঠিক ইনফরমেশন দিয়ে, এবং ঠিক उतनीবার যতবার দরকার?”
Mocks vs Stubs
এক অন্য বিভ্রান্তির পয়েন্ট হলো মক এবং স্টাব তুলনা করা।
পার্থক্য কী?
- একটি স্টাব শুধু একটি ক্যানড রেসপন্স 가진 মেথড, এটি আচরণের চিন্তা করে না।
- একটি মক এক্সপেক্ট করে মেথডগুলো কল হবে, যদি কল না হয় তবে টেস্ট ফেইল করবে।
RSpec-এ একটি স্টাব:
stub = double("json") allow(stub).to receive(:response) do {"blog"=>"rubyguides.com", "rating"=>"5/5"}.to_json end
allow মেথডটি এটিকে স্টাব বানায়।
আমরা আমাদের টেস্ট অবজেক্ট double("json")-কে এই মেথড রিসিভ ও রেসপন্ড করার অনুমতি দিচ্ছি, কিন্তু আমরা চেক করছি না যে এটি কল হচ্ছে কিনা।
এটিই পার্থক্য!
ভেরিফাইড ডাবলস কীভাবে ব্যবহার করবেন
মক ও স্টাবের একটি ডাউনসাইড হলো আপনি প্রোডাকশন কোডে অস্তিত্বহীন একটি মেথড ব্যবহার করতে পারেন।
কারণ মেথডের নাম পরিবর্তন হয়েছে… বা হয়তো আপনি টাইপো করলেন!
পরিচিত হন ভেরিফাইড ডাবলস-এর সাথে।
একটি ভেরিফাইড ডাবলকে আপনি অথবা স্টাব (allow) অথবা মক (expect) হিসেবে ব্যবহার করতে পারেন এবং এটি চেক করবে যে এই নামের একটি মেথড একজিস্ট করে।
উদাহরণ:
mock = instance_double(ImageProcessor)
যদি মেথডটি না থাকে তবে এটি এরর দেখাবে:
1) ImageFlipper calls the flip method with the correct arguments Failure/Error: expect(mock).to receive(:flip).with("ruby.jpg") the ImageProcessor class does not implement the instance method: flip
কিন্তু যদি মেথডটি থাকে তবে সঠিকভাবে কাজ করবে।
মক রিটার্নিং ভ্যালু
মকিং-এ ফিরি।
সর্বশ্রেষ্ঠ উদাহরণে আমাদের এটি ছিল:
expect(mock).to receive(:flip).with("ruby.jpg")
যখন আপনার কোড flip কল করে, মকটি nil রিটার্ন করবে।
যদি কোডটি nil ছাড়া অন্য কিছু এক্সপেক্ট করে তবে এরর হবে।
আপনি মককে একটি রেজাল্ট রিটার্ন করিয়ে এটি ঠিক করতে পারেন।
এভাবেই:
expect(mock).to receive(:flip).with("ruby.jpg").and_return("ruby-flipped.jpg")
ইনস্ট্যান্স মেথড মক করার পদ্ধতি
মান লিন আপনার এমন কোড আছে:
class NumberGenerator def random "A" * rand(1..10) end end
র্যান্ডমনেসের কারণে এই মেথডটি টেস্ট করা কঠিন।
RSpec আপনাকে rand মক বা স্টাব করতে দেয়।
এভাবেই:
it "generates a random number" do generator = NumberGenerator.new allow(generator).to receive(:rand).and_return(5) expect(generator.random).to eq("AAAAA") end
এখন:
rand একটি ফিক্সড ভ্যালু রিটার্ন করে তাই আপনি আপনার মেথডের আউটকাম টেস্ট করতে পারেন।
আদর্শভাবে, আপনি আপনার ডিপেন্ডেন্সি ইনজেক্ট করতে চাবেন (এই কেসে rand ফাংশন) যাতে আপনি এটিকে কন্ট্রোল করতে পারেন। ডিপেন্ডেন্সি ইনজেক্ট মানে আপনি এটি প্যারামিটার হিসেবে পাস করেন, কোনো ফ্যান্সি জিনিস নয়।
কিন্তু কখনো কখনো শুধু মেথডটি স্টাব করাই বেশি সুবিধাজনক।
কখন মক ব্যবহার করবেন?
এখন বড় প্রশ্ন…
ঠিক কখন আপনি মক ব্যবহার করবেন?
সফটওয়্যার ডেভেলপমেন্ট একটি জটিল বিষয়।
কিন্তু কয়েকটি গাইডলাইন আপনি ফলো করতে পারেন:
- যদি টেস্ট করা মেথড একটি ভ্যালু রিটার্ন করে এবং এর কোনো সাইড ইফেক্ট নেই (ফাইল তৈরি, API রিকোয়েস্ট ইত্যাদি) তবে আপনাকে মক দরকার নেই। শুধু রিটার্ন ভ্যালু চেক করুন।
- যদি মেথডটি এক্সটার্নাল অবজেক্টের সাথে কাজ করে এবং সেগুলোকে অর্ডার দেয়, তবে আপনি এই অবজেক্টের সাথে ইন্টারঅ্যাকশন মক করতে পারেন।
- যদি মেথডটি একটি এক্সটার্নাল সার্ভিস থেকে ডেটা রিকোয়েস্ট করে (যেমন API), তবে আপনি টেস্টিং উদ্দেশ্যে ডেটা প্রদানের জন্য একটি স্টাব ব্যবহার করতে পারেন।
আপনি বাহিরের দুনিয়ার সাথে ইন্টারঅ্যাকশনের জন্য মকিং রিজার্ভ রাখবেন।
অন্য কথায়…
আপনার নিজের অ্যাপ্লিকেশনের ক্লাসগুলো মক করবেন না!
কেন?
কারণ এটি আপনার টেস্টকে ইমপ্লিমেন্টেশন ডিটেইলসের সাথে কাপলিং করিয়ে দেয় এবং আপনার কোড পরিবর্তন করা কঠিন করে দেয়।
একমাত্র এক্সেপশন হলো ৩র্থ পার্টি কোডের জন্য র্যাপার ক্লাসগুলো।
আরও শিখতে এই ভিডিও দেখুন:
সারসংক্ষেপ
আপনি RSpec মক, স্টাব এবং ভেরিফাইড ডাবলস সম্পর্কে শিখেছেন!
দয়া করে এই আর্টিকেলটি শেয়ার করুন যাতে আরও মানুষ উপভোগ ও লাভ করতে পারে।
পড়ার জন্য ধন্যবাদ 🙂