হ্যালো হাসিব! আমরা Controller টেস্টিংয়ের আরেকটি রিয়েল-ওয়ার্ল্ড সিনারিওতে চলে এসেছি।

আগের লেকচারে আমরা Index মেথড (GET রিকোয়েস্ট) টেস্ট করেছিলাম, যা খুব সিম্পল ছিল। কিন্তু এই লেকচারে আমরা Create মেথড (POST রিকোয়েস্ট) টেস্ট করবো। POST রিকোয়েস্টে সাধারণত ডাটা ইনসার্ট করা হয় এবং এখানে Model Validation (ModelState) এর একটি বড় ভূমিকা থাকে।

চলো লেকচারটির একটি কুইক সামারি এবং বিস্তারিত পোস্টমর্টেম করে ফেলি।

📝 Lecture Summary at a Glance

  • Two Scenarios: Create (POST) মেথডের জন্য দুটি টেস্ট কেস লেখা হয়েছে: ১. যখন মডেলে এরর থাকে (Invalid ModelState) - তখন এটি একই View ফেরত দেয়। ২. যখন মডেল ভ্যালিড থাকে (No errors) - তখন এটি সাকসেসফুলি ডাটা সেভ করে Index পেজে রিডাইরেক্ট করে।
  • Mocking Dependencies: Create মেথডটি ইন্টারনালি CountriesService.GetAllCountries এবং PersonsService.AddPerson কল করে, তাই টেস্টের Arrange ফেজে এই দুটোকেই মক করা হয়েছে।
  • Injecting Model Errors: ইউনিট টেস্টে ASP.NET Core-এর ডিফল্ট মডেল বাইন্ডার কাজ করে না, তাই _personsController.ModelState.AddModelError() দিয়ে ম্যানুয়ালি এরর পুশ করে ইনভ্যালিড সিনারিও তৈরি করা হয়েছে।
  • Testing Redirects: যখন মেথড সাকসেসফুলি কাজ শেষ করে অন্য পেজে রিডাইরেক্ট করে, তখন RedirectToActionResult টাইপ চেক করে তার ActionName (“Index”) ভেরিফাই করা হয়েছে।

🧠 Comprehensive Breakdown & Deep Dive

১. Scenario 1: Invalid Model State [Importance: 9/10]

  • The “Why”: একজন ইউজার যদি ফর্ম সাবমিট করার সময় নাম ফাঁকা রাখে, তবে Controller-এর ModelState.IsValid প্রপার্টি false হয়ে যায় এবং ইউজারকে এরর মেসেজসহ আবার একই পেজ দেখানো হয়। আমরা এটাই টেস্ট করতে চাই।
  • The Trick: টেস্টিং এনভায়রনমেন্টে ModelState নিজে নিজে পপুলেট হয় না। তাই আমাদের কোড দিয়ে জোর করে একটি এরর ঢোকাতে হয়।

💻 Code Implementation:

[Fact]
public async Task Create_IfModelErrors_ToReturnCreateView()
{
    // Arrange: Mock setup...
    PersonAddRequest request = _fixture.Create<PersonAddRequest>();
    
    // The Trick: ম্যানুয়ালি মডেলে এরর পুশ করা
    _personsController.ModelState.AddModelError("PersonName", "Person Name can't be blank");
 
    // Act
    IActionResult result = await _personsController.Create(request);
 
    // Assert: চেক করা যে এটি ViewResult ফেরত দিয়েছে কিনা
    ViewResult viewResult = Assert.IsType<ViewResult>(result);
    // চেক করা যে একই মডেল ডাটা আবার ভিউতে পাঠানো হয়েছে কিনা
    viewResult.ViewData.Model.Should().BeAssignableTo<PersonAddRequest>();
    viewResult.ViewData.Model.Should().BeEquivalentTo(request);
}
 

২. Scenario 2: Valid Model State & Redirection [Importance: 10/10]

  • The “Why”: যদি ইউজার ফর্মে সব ডাটা ঠিকঠাক দেয়, তবে Controller প্রথমে সার্ভিস লেয়ারকে কল করে ডাটা সেভ করে, এরপর Index পেজে রিডাইরেক্ট করে দেয় (return RedirectToAction("Index"))।

💻 Code Implementation:

[Fact]
public async Task Create_IfNoModelErrors_ToReturnRedirectToIndex()
{
    // Arrange: Mock setup...
    PersonAddRequest request = _fixture.Create<PersonAddRequest>();
    // এখানে ModelState এ কোনো এরর অ্যাড করা হলো না (অটোমেটিক্যালি IsValid = true হবে)
 
    // Act
    IActionResult result = await _personsController.Create(request);
 
    // Assert: চেক করা যে এটি RedirectToActionResult ফেরত দিয়েছে কিনা
    RedirectToActionResult redirectResult = Assert.IsType<RedirectToActionResult>(result);
    // চেক করা যে ঠিক কোন মেথডে রিডাইরেক্ট হয়েছে
    redirectResult.ActionName.Should().Be("Index");
}
 

৩. The ActionMethod Overloading Mystery [Importance: 8/10]

  • The Question: Controller-এ তো Create নামে দুটি মেথড আছে (একটি GET, একটি POST)। আমরা যখন _personsController.Create(request) লিখি, তখন সে কীভাবে বোঝে যে POST মেথডটিকে কল করতে হবে?
  • The Answer: Method Overloading-এর কারণে। GET মেথডের কোনো প্যারামিটার নেই, কিন্তু POST মেথডটি প্যারামিটার হিসেবে PersonAddRequest রিসিভ করে। যেহেতু আমরা টেস্টে আর্গুমেন্ট পাস করছি, C# কম্পাইলার নিজে থেকেই POST ভার্সনটিকে কল করবে।

🚀 Modern .NET Architecture Pro Tips

হাসিব, Controller টেস্টিং নিয়ে লেকচারার ভিডিওর শেষে যে কথাটি বলেছেন তা খুবই ইম্পরট্যান্ট। Controller-এর ভেতরে মূলত Routing, Model Binding, Validation এবং Authorization এর কাজ থাকে। কিন্তু আমরা যখন এইভাবে Unit Test লিখি (সরাসরি _personsController.Create() কল করে), তখন এই ৪টি জিনিসের কোনোটিই টেস্ট হয় না! কারণ আমরা সরাসরি C# মেথড কল করছি, HTTP রিকোয়েস্ট পাঠাচ্ছি না।

এ কারণেই ইন্ডাস্ট্রিতে Controller-এর জন্য Unit Test এর চেয়ে Integration Test বেশি প্রেফার করা হয়। Integration Test-এ আমরা একটি ফেক ব্রাউজার বা HttpClient দিয়ে আসল POST /persons/create রিকোয়েস্ট পাঠাই, যা Controller-এর সব লেয়ারকে টেস্ট করে।

খুব সম্ভবত এই কোর্সের পরবর্তী লেকচারেই Integration Testing শুরু হতে যাচ্ছে, যা তোমার “Chatrabash” প্রজেক্টের API টেস্টিংয়ের জন্য জাস্ট গেম-চেঞ্জার হবে! তুমি রেডি হলে সেই ট্রান্সক্রিপ্টটি দিতে পারো।