আমি আপনার এক্সপার্ট সফটওয়্যার ইঞ্জিনিয়ারিং ট্রেইনার এবং লার্নিং ফ্যাসিলিটেটর। চলুন, আজকের লেকচারটি অত্যন্ত সহজ এবং গুছিয়ে ধাপে ধাপে বুঝে নেওয়া যাক।
কোর্স আউটলাইন অনুযায়ী আমরা এখন “Section 15: xUnit testing”-এর মধ্যে আছি। এই লেকচারে আমরা TDD (Test Driven Development) অ্যাপ্রোচ ব্যবহার করে GetCountryByCountryId মেথডের জন্য Unit Test লেখা শিখব।
📝 Lecture Summary (Quick Revision)
ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য সম্পূর্ণ লেকচারের মূল কাজগুলো নিচে লিস্ট আকারে দেওয়া হলো:
-
Interface Update:
ICountriesServiceইন্টারফেসেGetCountryByCountryIdমেথড ডিক্লেয়ার করা, যা ইনপুট হিসেবেGuid?নেবে এবংCountryResponse?রিটার্ন করবে। -
Dummy Implementation:
CountriesServiceক্লাসে মেথডটির ইনিশিয়াল বা ডামি ইমপ্লিমেন্টেশন করা (যেখানেNotImplementedExceptionথ্রো করা হয়)। -
Test Isolation Concept: XUnit-এ প্রতিটি Unit Test স্বাধীনভাবে (independently) কাজ করে। একটি টেস্টে অ্যাড করা ডেটা অন্য টেস্টে থাকে না।
-
Test Case 1 (Null ID):
GetCountryByCountryId_NullCountryIdমেথড তৈরি করে চেক করা যে ইনপুটnullদিলে মেথডটিnullরিটার্ন করে কিনা (UsingAssert.Null)। -
Test Case 2 (Valid ID):
GetCountryByCountryId_ValidCountryIdমেথড তৈরি করা। এখানে প্রথমেAddCountryদিয়ে একটি Country ইনসার্ট করা হয় এবং সেই ID দিয়েGetCountryByCountryIdকল করে চেক করা হয় ডেটা ঠিকমতো আসছে কিনা (UsingAssert.Equal)।
🧠 Comprehensive Breakdown
এই লেকচারে আমরা মূলত একটি স্পেসিফিক ID দিয়ে ডাটাবেস (বা লিস্ট) থেকে Country খুঁজে বের করার লজিক টেস্ট করব। চলুন প্রতিটি ধাপ বিস্তারিতভাবে বুঝে নিই।
১. Interface-এ Method ডিক্লেয়ারেশন এবং XML Comments (Priority: 7/10)
Concept & Why: কন্ট্রোলার থেকে যখন কোনো একটি নির্দিষ্ট Country-এর ID পাঠানো হবে, সার্ভিসকে ওই ID অনুযায়ী ডেটা খুঁজে বের করতে হবে। যদি ওই ID-এর কোনো Country না পাওয়া যায় বা ID null হয়, তবে Exception থ্রো করার বদলে মেথডটি null রিটার্ন করবে। তাই রিটার্ন টাইপ এবং ইনপুট প্যারামিটার উভয়কেই Nullable (?) করা হয়েছে।
C#
// ICountriesService.cs
/// <summary>
/// Returns a country object based on the given country id
/// </summary>
/// <param name="countryId">CountryID (guid) to search</param>
/// <returns>Matching country object or null</returns>
CountryResponse? GetCountryByCountryId(Guid? countryId);
২. TDD অনুযায়ী Dummy Implementation (Priority: 5/10)
Concept & Why: Test Driven Development (TDD)-এর নিয়ম হলো কোড লেখার আগেই তার টেস্ট লেখা। কিন্তু ইন্টারফেসে মেথড ডিক্লেয়ার করলে ইমপ্লিমেন্টেশন ক্লাসে সেটি না লিখলে কম্পাইলার এরর দেয়।
Visual Studio-তে ইন্টারফেসের ওপর কার্সর রেখে Quick Actions (Shortcut: Ctrl + .) ব্যবহার করে “Implement Interface” সিলেক্ট করলে স্বয়ংক্রিয়ভাবে একটি মেথড তৈরি হয়ে যায়।
C#
// CountriesService.cs
public CountryResponse? GetCountryByCountryId(Guid? countryId)
{
// Dummy implementation to satisfy the compiler temporarily
throw new NotImplementedException();
}
৩. Unit Test 1: Null CountryID চেক করা (Priority: 9/10)
Concept & Why: আমাদের প্রথম টেস্ট কেস হলো— যদি ইউজার ভুল করে null ভ্যালু পাঠায়, তবে সিস্টেম ক্র্যাশ না করে যেন সুন্দরভাবে null রিটার্ন করে।
C#
[Fact]
public void GetCountryByCountryId_NullCountryId()
{
// Arrange
Guid? countryID = null;
// Act
CountryResponse? country_response_from_get = _countriesService.GetCountryByCountryId(countryID);
// Assert
Assert.Null(country_response_from_get);
}
৪. Unit Test 2: Valid CountryID দিয়ে ডেটা রিট্রিভ করা (Priority: 10/10)
Concept & Why: এটি সবচেয়ে গুরুত্বপূর্ণ অংশ। xUnit-এ প্রতিটি Unit Test Case সম্পূর্ণ স্বাধীনভাবে রান করে। অর্থাৎ, আগের কোনো টেস্টে আপনি যদি ১০টা Country অ্যাড করেও থাকেন, এই নতুন টেস্ট মেথড রান করার সময় ডাটা স্টোর (লিস্ট) সম্পূর্ণ ফাঁকা (empty) থাকবে।
এই Test Isolation-এর কারণে আমাদের প্রথমে একটি Country অ্যাড (Arrange) করে নিতে হবে। তারপর সেই জেনারেট হওয়া ID ব্যবহার করে ডেটা তুলে আনতে (Act) হবে। এবং সবশেষে Expected এবং Actual ডেটা মেলাতে (Assert) হবে।
C#
[Fact]
public void GetCountryByCountryId_ValidCountryId()
{
// Arrange: একটি নতুন Country অ্যাড করা হচ্ছে
CountryAddRequest country_request = new CountryAddRequest() { CountryName = "China" };
CountryResponse country_response_from_add = _countriesService.AddCountry(country_request);
// Act: অ্যাড করা Country এর ID দিয়ে ডেটা রিট্রিভ করা হচ্ছে
CountryResponse? country_response_from_get = _countriesService.GetCountryByCountryId(country_response_from_add.CountryID);
// Assert: অ্যাড করা ডেটা এবং রিট্রিভ করা ডেটা হুবহু এক কিনা তা চেক করা হচ্ছে
Assert.Equal(country_response_from_add, country_response_from_get);
}
৫. Assert.Equal এর ভেতরের মেকানিজম (Priority: 10/10)
Concept: আগের লেকচারের সাথে এই লেকচারের একটি বড় কানেকশন আছে। Assert.Equal(expected, actual) মেথডটি ইন্টারনালি দুটি অবজেক্টের মধ্যে Equals মেথড কল করে।
আগের লেকচারে আমরা CountryResponse ক্লাসে Equals মেথডটি Override করেছিলাম, যেন এটি মেমরি রেফারেন্স (Reference Equality) চেক না করে ভেতরের CountryID এবং CountryName (Value Equality) চেক করে। ওই Override করা Equals মেথডটি থাকার কারণেই Assert.Equal এখানে সফলভাবে দুটি ভিন্ন রেফারেন্সের অবজেক্টকে সমান প্রমাণ করতে পারবে (আগামী লেকচারে যখন মেথডটি ইমপ্লিমেন্ট করা হবে)।
🚀 Modern C# (.NET 8/10) Updates & Smarter Approach
লেকচারের কোডটি চমৎকার, তবে মডার্ন .NET এবং C# এর নতুন ভার্সনগুলো ব্যবহার করে টেস্টিং এবং অবজেক্ট কম্পারিজন আরও স্মার্টভাবে করা যায়।
1. Using Assert.Equivalent (xUnit Update):
আগের লেকচারে আমাদের ম্যানুয়ালি Equals মেথড ওভাররাইড করতে হয়েছিল। কিন্তু xUnit-এর নতুন ভার্সনগুলোতে Assert.Equivalent() মেথড আনা হয়েছে। আপনি যদি Equals মেথড ওভাররাইড নাও করেন, তবুও Assert.Equivalent() দুটি অবজেক্টের ভেতরের প্রপার্টিগুলোর ভ্যালু (Value Equality) চেক করতে পারে!
C#
// Modern way to check object equivalence without overriding Equals
Assert.Equivalent(country_response_from_add, country_response_from_get);
2. record Type in C#:
আপনার CountryResponse যদি একটি record হয় (যা .NET 9/10 এ DTO এর জন্য স্ট্যান্ডার্ড), তবে Assert.Equal ডিফল্টভাবেই কাজ করবে, কারণ record-এর ডিফল্ট বিহেভিয়ারই হলো Value Equality চেক করা। কোনো কাস্টম লজিকের প্রয়োজন হবে না।
🏆 Best Practices for Unit Testing (xUnit)
-
Test Isolation: সবসময় মনে রাখবেন, একটি Unit Test যেন অন্য কোনো Unit Test-এর ওপর নির্ভরশীল (dependent) না হয়। প্রতিটি টেস্টের নিজস্ব ডেটা সেটআপ (Arrange) থাকতে হবে, ঠিক যেমন লেকচারে দ্বিতীয় টেস্টটিতে করা হয়েছে।
-
Naming Convention: টেস্টের নাম দেখেই যেন এর উদ্দেশ্য বোঝা যায়। যেমন:
MethodName_Condition_ExpectedResult(e.g.,GetCountryByCountryId_NullCountryId_ReturnsNull). -
Use Specific Asserts: ভ্যালু চেক করার জন্য
Assert.True(obj == null)লেখার চেয়ে সরাসরিAssert.Null(obj)ব্যবহার করা অনেক বেশি রিডেবল এবং স্ট্যান্ডার্ড। -
Arrange-Act-Assert (AAA): সবসময় আপনার টেস্ট কোডকে এই তিনটি ব্লকে ভাগ করে লিখবেন এবং প্রয়োজনে কমেন্ট দিয়ে সেপারেশন রাখবেন, এতে কোড অনেক বেশি ক্লিন দেখায়।