স্বাগতম! আপনি এখন আপনার কোর্সের Section 23: SOLID Principles এর Interface Segregation Principle (ISP) in Tests লেকচারে আছেন। গত লেকচারে আমরা মূল প্রজেক্টে (Service এবং Controller) বিশাল IPersonsService কে ভেঙে ৫টি ছোট ইন্টারফেসে (Getter, Adder, Updater, Deleter, Sorter) ভাগ করেছিলাম।
যেহেতু মূল প্রোজেক্টের আর্কিটেকচার পরিবর্তন হয়েছে, তাই আমাদের Unit Test এবং Integration Test প্রজেক্টেও কিছু Compilation Error দেখা দিয়েছে। আজকের লেকচারে আমরা শিখব কীভাবে সেই Error গুলো ফিক্স করতে হয় এবং IoC Container-এ নতুন Service গুলো রেজিস্টার করতে হয়। চলুন শুরু করি!
📝 Quick Summary for Revision
ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য পুরো লেকচারের মূল বিষয়গুলো নিচে তালিকাভুক্ত করা হলো:
- The Problem:
IPersonsServiceকে ভেঙে ৫টি ইন্টারফেস করায় Test Project (ServiceTest এবং ControllerTest) এ Compilation Error দেখা দেয়। - Service Test Fix:
PersonsServiceTestএ আগের একটি ক্লাসের পরিবর্তে এখন ৫টি আলাদা Service Object (Getter, Adder, Deleter, Sorter, Updater) তৈরি করতে হবে এবং সংশ্লিষ্ট টেস্ট কেসে সঠিক অবজেক্ট কল করতে হবে। - Controller Test Fix:
PersonsControllerTestএ Controller কে ইনিশিয়ালাইজ করার সময় কনস্ট্রাক্টরে ৫টি Mocked Service (Mock) পাস করতে হবে। - IoC Container Registration (Critical): নতুন তৈরি করা ৫টি সার্ভিসকে অবশ্যই
ConfigureServicesExtensionবাProgram.csফাইলেAddScopedবাAddTransientদিয়ে রেজিস্টার করতে হবে। - Integration Test Fix: AppSettings.json ফাইলে ভুল Database Name থাকলে সেটি ঠিক করে নিতে হবে।
- Assignment (Practice):
ICountriesServiceকে ভেঙেCountriesAdderService,CountriesGetterServiceএবংCountriesUploaderServiceএ ভাগ করা।
🧠 Comprehensive Breakdown
লেকচারের প্রতিটি বিষয় নিচে বিস্তারিতভাবে আলোচনা করা হলো এবং বোঝার সুবিধার্থে প্রাসঙ্গিক Code Implementation যুক্ত করা হলো।
1. Fixing PersonsServiceTest [Priority: 10/10]
Why: আগে আমাদের টেস্ট ক্লাসটি শুধুমাত্র একটি Service Object (_personsService) তৈরি করত। কিন্তু এখন যেহেতু সার্ভিস ৫ ভাগে বিভক্ত, তাই আমাদের ৫টি আলাদা Object তৈরি করতে হবে।
Step 1: Removing unused namespaces
যেহেতু আমরা এখন আর DbContext সরাসরি মক করছি না, তাই ফাইলের উপর থেকে Microsoft.EntityFrameworkCore এবং EntityFrameworkCoreMock মুছে ফেলা হয়েছে।
Step 2: Creating Service Objects কনস্ট্রাক্টরের ভেতরে ৫টি আলাদা সার্ভিসের অবজেক্ট তৈরি করা হয়েছে এবং সেগুলোতে Mocked Repository এবং Logger পাস করা হয়েছে।
// Example of how the objects are initialized in the constructor
_personsGetterService = new PersonsGetterService(_personsRepositoryMock.Object, _loggerMock.Object);
_personsAdderService = new PersonsAdderService(_personsRepositoryMock.Object, _loggerMock.Object);
_personsUpdaterService = new PersonsUpdaterService(_personsRepositoryMock.Object, _loggerMock.Object);
_personsDeleterService = new PersonsDeleterService(_personsRepositoryMock.Object, _loggerMock.Object);
_personsSorterService = new PersonsSorterService(_personsRepositoryMock.Object, _loggerMock.Object);
Step 3: Updating the Test Methods
এখন প্রতিটি [Fact] বা টেস্ট মেথডের ভেতরে গিয়ে সঠিক Service Object কল করতে হবে।
AddPersonমেথড টেস্ট করার জন্য_personsAdderService.AddPerson()কল করা হয়েছে।DeletePersonটেস্ট করার জন্য_personsDeleterService.DeletePerson()কল করা হয়েছে।
2. Fixing PersonsControllerTest [Priority: 10/10]
Why: PersonsController এর কনস্ট্রাক্টরে এখন ৫টি ডিপেন্ডেন্সি প্রয়োজন। তাই টেস্ট করার সময় আমাদের ৫টি Mock Object তৈরি করে কন্ট্রোলারে পাস করতে হবে।
Step 1: Creating Mock Objects Moq লাইব্রেরি ব্যবহার করে ৫টি ইন্টারফেসের মক তৈরি করা হয়েছে।
private readonly Mock<IPersonsGetterService> _personsGetterServiceMock;
private readonly Mock<IPersonsAdderService> _personsAdderServiceMock;
// ... (same for other 3 services)
Step 2: Passing Mocks to the Controller
PersonsController এর অবজেক্ট তৈরি করার সময় mock.Object প্রোপার্টি ব্যবহার করে সবগুলো ডিপেন্ডেন্সি পাস করা হয়েছে।
PersonsController personsController = new PersonsController(
_personsGetterServiceMock.Object,
_personsAdderServiceMock.Object,
_personsUpdaterServiceMock.Object,
_personsDeleterServiceMock.Object,
_personsSorterServiceMock.Object,
_countriesServiceMock.Object,
_loggerMock.Object
);
Step 3: Setup Specific Mocks
যে টেস্টে যে মেথড মক করা প্রয়োজন, সেখানে নির্দিষ্ট Mock Object ব্যবহার করা হয়েছে। যেমন, Add অপারেশনের টেস্টে _personsAdderServiceMock.Setup() ব্যবহার করা হয়েছে।
3. Fixing the Dependency Injection Error (Critical Step) [Priority: 10/10]
সব কোড ঠিক করার পর অ্যাপ্লিকেশন রান করলে একটি Exception আসে: “Unable to resolve the service type for IPersonsAdderService”.
Why: কারণ আমরা ইন্টারফেসগুলো তৈরি করেছি ঠিকই, কিন্তু ASP.NET Core এর Inversion of Control (IoC) Container-কে জানাইনি যে কোন ইন্টারফেসের বদলে কোন ক্লাস দিতে হবে।
The Fix: ConfigureServicesExtension.cs (বা আপনার ভার্সন অনুযায়ী Program.cs) ফাইলে গিয়ে সার্ভিসগুলো রেজিস্টার করতে হবে।
// Registering all segregated services in the IoC Container
services.AddScoped<IPersonsGetterService, PersonsGetterService>();
services.AddScoped<IPersonsAdderService, PersonsAdderService>();
services.AddScoped<IPersonsUpdaterService, PersonsUpdaterService>();
services.AddScoped<IPersonsDeleterService, PersonsDeleterService>();
services.AddScoped<IPersonsSorterService, PersonsSorterService>();
4. AppSettings & Integration Test Issue [Priority: 5/10]
Integration Test রান করার সময় Internal Server Error (500) দেখা যাচ্ছিল। এর কারণ ছিল appsettings.json ফাইলে ডাটাবেসের নাম ভুল দেওয়া ছিল (আগের কোনো লেকচারে Exception Handling দেখানোর জন্য হয়তো ভুল নাম দেওয়া হয়েছিল)।
- Fix: Connection String এ ডাটাবেসের নাম ঠিক করে প্রকৃত ডাটাবেসের নাম দেওয়া হয়েছে, এরপর Integration Test পাস করেছে।
5. Practice Assignment [Priority: 8/10]
লেকচারার আপনাকে একটি টাস্ক দিয়েছেন: ICountriesService কে ISP অনুযায়ী ভাগ করা।
- Task:
ICountriesServiceকে ভেঙে ৩টি ইন্টারফেস বানাতে হবে:
ICountriesGetterService(GetAllCountries, GetCountryByCountryId)ICountriesAdderService(AddCountry)ICountriesUploaderService(UploadCountriesFromExcel)
🌟 Best Practices
- Test Maintenance: যখনই মূল আর্কিটেকচারে কোনো বড় পরিবর্তন (যেমন ISP) করবেন, তখনই সাথে সাথে Test Project গুলো আপডেট করে নিবেন। এতে পরে বিশাল সংখ্যক Error একসাথে হ্যান্ডেল করার ঝামেলা থাকে না।
- Mock Only What’s Needed: Controller Test-এ সবগুলো Service Mock করতে হলেও,
Setup()শুধুমাত্র সেই সার্ভিসটির জন্যই করবেন যা ঐ নির্দিষ্ট টেস্ট মেথডে কল করা হচ্ছে।
🚀 .NET 10 Context
Dependency Injection এর ক্ষেত্রে .NET 10 এও সার্ভিস রেজিস্টার করার প্রক্রিয়া একই রকম (যেমন builder.Services.AddScoped<IInterface, Class>())। তবে .NET 10 এর লেটেস্ট টেস্টিং ফ্রেমওয়ার্কগুলোতে (যেমন xUnit 3) Constructor Injection এবং Fixture সেটআপ আরও ক্লিন করা যায়, যা অনেকগুলো Dependency একবারে মক করার প্রক্রিয়াকে সহজ করে তোলে।