হ্যালো! আমি তোমার Simple Coding Tutor। আজ আমরা Section 15-এর অত্যন্ত গুরুত্বপূর্ণ লেকচার “Add Country - xUnit Test - Part 3” নিয়ে আলোচনা করব। এই লেকচারে আমরা TDD (Test-Driven Development) পদ্ধতি অনুসরণ করে AddCountry মেথডের জন্য ৪টি ভিন্ন টেস্ট কেস (Test Cases) কোড করব এবং টেস্ট রানার দিয়ে সেগুলো রান করে দেখব।
চলো, প্রথমে পুরো লেকচারের একটি সংক্ষিপ্ত সামারি দেখে নিই।
📝 লেকচার সামারি (Summary for Quick Revision)
ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য পুরো লেকচারের মূল বিষয়গুলো নিচে তালিকাভুক্ত করা হলো:
- Project Reference Setup:
Testপ্রজেক্টেServicesপ্রজেক্টের রেফারেন্স যুক্ত করা হয়েছে, যার ফলে টেস্ট প্রজেক্ট এখন তিনটি প্রজেক্টকে (Entities,ServiceContracts,Services) চেনে। - Four Core Test Cases:
AddCountryমেথডের জন্য ৪টি টেস্ট রিকোয়ারমেন্ট ডিফাইন করা হয়েছে:
CountryAddRequestঅবজেক্টটিnullহলেArgumentNullExceptionথ্রো করবে।CountryNameপ্রপার্টিটিnullহলেArgumentExceptionথ্রো করবে।CountryNameডুপ্লিকেট (আগে থেকেই লিস্টে থাকলে) হলেArgumentExceptionথ্রো করবে।- সঠিক
CountryNameদিলে ডেটা সফলভাবে সেভ হবে এবং একটি ভ্যালিড, অ-শূন্য (Non-empty) GUID জেনারেট হয়েCountryResponseরিটার্ন করবে।
- xUnit Features Used: টেস্ট মেথডগুলোকে চিহ্নিত করতে
[Fact]attribute, এক্সেপশন টেস্ট করার জন্যAssert.Throws, এবং কন্ডিশন চেক করার জন্যAssert.Trueব্যবহার করা হয়েছে। - The “Red” Phase: সব টেস্ট রান করার পর সবগুলোই ফেইল (Fail) করেছে এবং
NotImplementedExceptionদেখিয়েছে, যা TDD সাইকেলের প্রথম এবং স্বাভাবিক ধাপ।
🧠 Comprehensive Breakdown (বিস্তারিত আলোচনা)
১. Test Project-এ Services-এর Reference যুক্ত করা [Priority: 8/10]
টেস্ট প্রজেক্ট থেকে আসল সার্ভিস ক্লাসকে টেস্ট করার জন্য Services প্রজেক্টের রেফারেন্স প্রয়োজন।
- কেন প্রয়োজন? আগের লেকচারে আমরা টেস্ট ক্লাসে
ICountryServiceইন্টারফেস ডিক্লেয়ার করেছিলাম। কিন্তু ইন্টারফেসের তো নিজস্ব কোনো কার্যকারিতা নেই। তার জন্য আসল ক্লাসCountriesService-এর অবজেক্ট তৈরি করতে হবে। - কিভাবে করা হয়েছে: Test প্রজেক্টের
Dependencies-এ Right-click ->Add Project Reference->Servicesপ্রজেক্ট সিলেক্ট করে OK করা হয়েছে। এখন টেস্ট প্রজেক্টের কনস্ট্রাক্টরে আমরা নিচের কোডটি লিখতে পারি:
private readonly ICountryService _countryService;
public CountriesServiceTest()
{
// আসল সার্ভিস ক্লাসের অবজেক্ট তৈরি করা হলো
_countryService = new CountriesService();
}
২. টেস্ট কেস ১: Null Request হ্যান্ডলিং [Priority: 9/10]
ইউজার বা কন্ট্রোলার যদি ভুল করে কোনো ডেটা না পাঠিয়ে সম্পূর্ণ null অবজেক্ট সার্ভিসে পাঠায়, তবে সিস্টেমকে ক্র্যাশ না করে একটি নির্দিষ্ট এক্সেপশন থ্রো করতে হবে।
- কেন? C#-এ এটি একটি স্ট্যান্ডার্ড প্র্যাকটিস যে মেথডের রিকোয়ার্ড আর্গুমেন্ট
nullহলেArgumentNullExceptionথ্রো করা হয়। - কোড ইমপ্লিমেন্টেশন:
[Fact]
public void AddCountry_NullCountry()
{
// Arrange
CountryAddRequest? request = null;
// Act & Assert (একত্রে ল্যাম্বডা এক্সপ্রেশনের মাধ্যমে)
Assert.Throws<ArgumentNullException>(() =>
{
_countryService.AddCountry(request);
});
}
৩. টেস্ট কেস ২: Null Country Name হ্যান্ডলিং [Priority: 9/10]
হতে পারে আর্গুমেন্ট অবজেক্টটি null নয়, কিন্তু তার ভেতরের CountryName প্রপার্টিটি null বা খালি।
- কেন? দেশের নাম ছাড়া কোনো দেশের এন্ট্রি ডেটাবেসে হওয়া সম্ভব নয়। তাই এক্ষেত্রে
ArgumentExceptionথ্রো করা উচিত। - কোড ইমপ্লিমেন্টেশন:
[Fact]
public void AddCountry_CountryNameIsNull()
{
// Arrange
CountryAddRequest request = new CountryAddRequest() { CountryName = null };
// Act & Assert
Assert.Throws<ArgumentException>(() =>
{
_countryService.AddCountry(request);
});
}
৪. টেস্ট কেস ৩: ডুপ্লিকেট দেশের নাম প্রতিরোধ করা [Priority: 9/10]
একই নামের দেশ ডেটাবেসে একাধিকবার যুক্ত হতে দেওয়া যাবে না। যেমন- লিস্টে একবার “USA” থাকলে দ্বিতীয়বার “USA” অ্যাড করতে গেলে সিস্টেম এরর দেবে।
- কেন? ডেটার ইউনিকনেস (Uniqueness) বজায় রাখার জন্য এটি অত্যন্ত জরুরি।
- কোড ইমপ্লিমেন্টেশন:
[Fact]
public void AddCountry_DuplicateCountryName()
{
// Arrange
CountryAddRequest request1 = new CountryAddRequest() { CountryName = "USA" };
CountryAddRequest request2 = new CountryAddRequest() { CountryName = "USA" };
// Act
_countryService.AddCountry(request1); // প্রথমবার সফলভাবে অ্যাড হওয়া উচিত (ধরে নেওয়া হচ্ছে)
// Assert (দ্বিতীয়বার একই নাম দিলে এক্সেপশন আশা করছি)
Assert.Throws<ArgumentException>(() =>
{
_countryService.AddCountry(request2);
});
}
৫. টেস্ট কেস ৪: সঠিক ডেটা ইনপুট (Success Path) [Priority: 10/10]
সব ভ্যালিডেশন পার হয়ে যখন সঠিক দেশের নাম (যেমন- “Japan”) দেওয়া হবে, তখন সেটি সফলভাবে সিস্টেমে যুক্ত হতে হবে।
- কেন? এটি হলো আমাদের মেথডের মূল বা হ্যাপি পাথ (Happy Path)। টেস্ট করতে হবে যে মেথডটি যুক্ত করার পর একটি নতুন
Guidআইডি তৈরি করছে কি না এবং তাCountryResponseআকারে ফেরত দিচ্ছে কি না। - কোড ইমপ্লিমেন্টেশন:
[Fact]
public void AddCountry_ProperCountryDetails()
{
// Arrange
CountryAddRequest request = new CountryAddRequest() { CountryName = "Japan" };
// Act
CountryResponse response = _countryService.AddCountry(request);
// Assert (চেক করছি আইডিটি জেনারেট হয়েছে কি না এবং তা খালি কি না)
Assert.True(response.CountryId != Guid.Empty);
}
৬. টেস্ট রান করা এবং TDD-এর “Red” ফেজ [Priority: 8/10]
সবগুলো টেস্ট মেথডের উপরে [Fact] অ্যাট্রিবিউট দেওয়ার পর সেগুলো Visual Studio-র Test Explorer-এ দৃশ্যমান হয়।
- ফলাফল: সবগুলো টেস্ট রান করার পর প্রতিটি টেস্ট ফেইল (Fail) করেছে।
- কারণ: আমাদের
CountriesServiceক্লাসের মেথডে এখনো কোনো আসল কোড লেখা নেই, সেখানে শুধুthrow new NotImplementedException()লেখা আছে। - TDD ফিলোসফি: এটিই হলো TDD-এর সৌন্দর্য (Red-Green-Refactor সাইকেল)। প্রথমে আমরা টেস্ট লিখে নিশ্চিত হলাম যে আমাদের রিকোয়ারমেন্টগুলো কী কী। পরের লেকচারে আমরা সার্ভিস ক্লাসে এমনভাবে কোড লিখব যেন এই লাল (Failed) টেস্টগুলো সব সবুজ (Passed) হয়ে যায়।
⌨️ IDE Shortcut Tricks:
- Visual Studio:
Test Explorerউইন্ডোটি ওপেন করার শর্টকাট হলোCtrl + E, T। সবগুলো টেস্ট একসাথে রান করার শর্টকাট হলোCtrl + R, A। - Visual Studio Code (VS Code): VS Code-এ টেস্ট রান করার জন্য প্রথমে
Testingসাইডবার আইকনে ক্লিক করতে পারো অথবাCtrl + Shift + PচেপেTest: Run All Testsকমান্ডটি এক্সিকিউট করতে পারো। কুইক অ্যাকশন বা মিসিং নেমস্পেস ইমপোর্ট (using xunit;) করার শর্টকাট হলোCtrl + .(Windows) বাCmd + .(Mac)।
💡 Best Practices & .NET 10 Updates
Best Practices (উদাহরণসহ):
- Unit Test Naming Convention: টেস্ট মেথডের নাম সবসময় স্পষ্ট হতে হবে।
AddCountry_NullCountryবাAddCountry_ProperCountryDetailsদেখেই বোঝা যাচ্ছে কোন মেথডের কোন স্টেট টেস্ট করা হচ্ছে। কখনোTestMethod1বাTest1নাম দেবে না। - Isolate Assertions: টেস্ট কেস ৪-এ আমরা চেক করেছি
response.CountryId != Guid.Empty। সাকসেস পাথে অবজেক্টটি নিজেইnullকি না তাও চেক করা ভালো প্র্যাকটিস।
.NET 10 Context (Modern Testing using Fluent Assertions):
বর্তমানে .NET 10 প্রজেক্টগুলোতে xUnit-এর ডিফল্ট Assert.True বা Assert.Throws এর চেয়ে FluentAssertions নামক একটি নুগেট প্যাকেজ ব্যাপক জনপ্রিয়। এটি কোডকে মানুষের পড়ার ভাষার মতো (Human-readable) সহজ করে তোলে।
যেমন, .NET 10-এ Fluent Assertions ব্যবহার করে সাকসেস পাথের টেস্টটি আরও মডার্নভাবে এভাবে লেখা যায়:
// .NET 10 Modern Unit Testing Style with FluentAssertions
[Fact]
public void AddCountry_ProperCountryDetails_Modern()
{
// Arrange
CountryAddRequest request = new() { CountryName = "Japan" };
// Act
CountryResponse response = _countryService.AddCountry(request);
// Fluent Assertions style (Much more readable!)
response.Should().NotBeNull();
response.CountryId.Should().NotBeEmpty();
response.CountryName.Should().Be("Japan");
}