হ্যালো হাসিব! আমরা টেস্টিংয়ের আরেকটি লেভেলে চলে এসেছি। আগের সেকশনগুলোতে আমরা Service লেয়ার টেস্ট করার সময় Repository কে মক (Mock) করেছিলাম। এই লেকচারটিতে আমরা শিখবো কীভাবে Controller টেস্ট করতে হয় এবং Controller কে টেস্ট করার সময় কীভাবে তার নিচের লেয়ার অর্থাৎ Service কে মক করতে হয়।
যেহেতু Controller সরাসরি ইউজার রিকোয়েস্ট হ্যান্ডেল করে এবং View রিটার্ন করে, তাই এর টেস্টিং স্ট্র্যাটেজি একটু ভিন্ন। চলো লেকচারটির একটি কুইক সামারি এবং বিস্তারিত পোস্টমর্টেম করে ফেলি।
📝 Lecture Summary at a Glance
- The Goal:
PersonsController-এরIndexঅ্যাকশন মেথডকে টেস্ট করা, যাতে কনফার্ম হওয়া যায় যে মেথডটি সঠিক ভিউ (ViewResult) এবং সঠিক ডেটা (Model) রিটার্ন করছে। - Project Reference:
CRUDTestsপ্রজেক্ট থেকে মেইন ওয়েব প্রজেক্ট (CRUDExample) এর রেফারেন্স অ্যাড করা হয়েছে যাতে Controller-কে অ্যাক্সেস করা যায়। - Mocking the Service: Controller-এর কনস্ট্রাক্টরে আসল সার্ভিসের বদলে
Mock<IPersonsService>এবংMock<ICountriesService>এর.Objectপাস করা হয়েছে। - Testing ViewResult & Model:
IActionResultকেViewResult-এ কাস্ট করে তার ভেতরেরViewData.Modelপ্রপার্টি চেক করা হয়েছে।
🧠 Comprehensive Breakdown & Deep Dive
১. Mocking the Service Layer [Importance: 10/10]
- The “Why”: Controller-এর কাজ হলো রিকোয়েস্ট রিসিভ করা এবং সার্ভিসকে কল করে ডাটা নিয়ে এসে View-কে দিয়ে দেওয়া। Controller টেস্ট করার সময় আমরা চাই না সার্ভিস লেয়ার কোনো কমপ্লেক্স ক্যালকুলেশন বা ডাটাবেস কল করুক। তাই আমরা সার্ভিস মেথডগুলোকে মক করে দিই।
💻 Code Implementation (Arrange):
// ১. AutoFixture দিয়ে ডামি ডাটা তৈরি করা
var personResponseList = _fixture.Create<List<PersonResponse>>();
// ২. Controller যে সার্ভিস মেথডগুলো কল করবে (GetFilteredPersons, GetSortedPersons), সেগুলোকে মক করা
_personsServiceMock
.Setup(temp => temp.GetFilteredPersons(It.IsAny<string>(), It.IsAny<string>()))
.ReturnsAsync(personResponseList); // ডামি ডাটা রিটার্ন করবে
_personsServiceMock
.Setup(temp => temp.GetSortedPersons(It.IsAny<List<PersonResponse>>(), It.IsAny<string>(), It.IsAny<SortOrderOptions>()))
.ReturnsAsync(personResponseList); // ডামি ডাটা রিটার্ন করবে
২. Invoking the Action Method [Importance: 9/10]
- The “Why”: Controller-কে টেস্ট করার জন্য আমাদের ম্যানুয়ালি তার অ্যাকশন মেথডটি কল করতে হবে।
💻 Code Implementation (Act):
// ১. AutoFixture দিয়ে প্যারামিটারের জন্য কিছু র্যান্ডম স্ট্রিং জেনারেট করা
string searchBy = _fixture.Create<string>();
string searchString = _fixture.Create<string>();
string sortBy = _fixture.Create<string>();
SortOrderOptions sortOrder = _fixture.Create<SortOrderOptions>();
// ২. অ্যাকশন মেথড কল করা
IActionResult result = await _personsController.Index(searchBy, searchString, sortBy, sortOrder);
৩. Verifying the View and Model (The Assert Phase) [Importance: 10/10]
- The “Why”: এটি Controller টেস্টিংয়ের সবচেয়ে গুরুত্বপূর্ণ পার্ট।
Indexমেথডটি একটিViewরিটার্ন করে যার ভেতরে মডেল হিসেবে একটিList<PersonResponse>থাকে। আমাদের চেক করতে হবে যে: ১. রিটার্ন টাইপটি আসলেইViewResultকিনা। ২. View এর মডেলটি একটি কালেকশন কিনা (IEnumerable<PersonResponse>)। ৩. মডেলে থাকা ডাটা আর আমাদের বানানো মক ডাটা হুবহু এক কিনা।
💻 Code Implementation (Assert):
using Microsoft.AspNetCore.Mvc;
// ১. চেক করা যে এটি ViewResult এবং সেটিকে কাস্ট (Typecast) করা
ViewResult viewResult = Assert.IsType<ViewResult>(result);
// ২. ViewData.Model থেকে ডাটাগুলো বের করা এবং কাস্ট করা
viewResult.ViewData.Model.Should().BeAssignableTo<IEnumerable<PersonResponse>>();
var returnedModel = viewResult.ViewData.Model as IEnumerable<PersonResponse>;
// ৩. ডাটা ভেরিফাই করা (FluentAssertions দিয়ে)
returnedModel.Should().BeEquivalentTo(personResponseList);
(এই কোডটি পাস করার মানে হলো—তোমার Controller একদম নিখুঁতভাবে সার্ভিস থেকে ডাটা নিয়ে View-তে পাঠাচ্ছে!)
🚀 Modern .NET Architecture Notes & Pro Tips
১. Integration Testing vs Unit Testing Controllers:
হাসিব, লেকচারার এখানে Controller-এর Unit Test দেখাচ্ছেন, যেখানে আমরা মেথডগুলোকে সরাসরি কল করছি (_personsController.Index(...))।
কিন্তু রিয়েল-ওয়ার্ল্ড এন্টারপ্রাইজ প্রোজেক্টে Controller-এর Unit Test খুব বেশি লেখা হয় না। এর বদলে Integration Test লেখা হয়। ইন্টিগ্রেশন টেস্টে HttpClient ব্যবহার করে একটি রিয়েল HTTP রিকোয়েস্ট (যেমন: GET /persons/index) পাঠানো হয় এবং রেসপন্সের স্ট্যাটাস কোড (200 OK) চেক করা হয়। এটি আরও বেশি রিলায়েবল কারণ এটি রাউটিং (Routing) এবং মডেল বাইন্ডিংও (Model Binding) টেস্ট করে।
(আশা করি এই কোর্সের পরবর্তী কোনো সেকশনে ইন্টিগ্রেশন টেস্টিং নিয়ে আলোচনা করা হবে)।
২. Understanding ViewData.Model:
আমরা যখন Controller থেকে return View(persons) লিখি, তখন ASP.NET Core ইন্টারনালি ওই persons লিস্টটাকে ViewData.Model প্রপার্টিতে সেভ করে রাখে। এ কারণেই টেস্ট করার সময় আমরা ডাটাগুলো সরাসরি result.Model না লিখে result.ViewData.Model থেকে বের করে আনছি।
পরবর্তী লেকচারে Create অ্যাকশন মেথডের (যা একটি POST রিকোয়েস্ট) টেস্টিং দেখানো হবে। তুমি রেডি হলে পরের ট্রান্সক্রিপ্টটি দিতে পারো!