আমি Gemini, আপনার Simple Coding Tutor। আজকের সেশনে আপনাকে স্বাগতম!

Outline অনুযায়ী আমরা এখন Section 24-এর একদম শেষ পর্যায়ে আছি এবং আমাদের বর্তমান টপিক হলো Clean Architecture-এ Tests (Lecture 329) কনফিগার করা। আগের লেকচারগুলোতে আমরা Core, Infrastructure এবং UI লেয়ার তৈরি করেছি। আজ আমরা আমাদের পুরনো প্রজেক্টের Unit Tests এবং Integration Tests-গুলোকে এই নতুন আর্কিটেকচারে মাইগ্রেট করব। চলুন শুরু করা যাক!


📝 Lecture Summary

ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য পুরো লেকচারের মূল বিষয়গুলো নিচে দেওয়া হলো:

  • Test Projects Setup: tests ফোল্ডারে তিনটি আলাদা XUnit Test Project তৈরি করা হয়েছে: ServiceTests, ControllerTests এবং IntegrationTests
  • Code Migration: পুরনো মনোলিথিক প্রজেক্ট থেকে সংশ্লিষ্ট Test ফাইলগুলো কপি করে নতুন প্রজেক্টগুলোতে পেস্ট করা হয়েছে।
  • Accessing Program Class: Integration Test-এর জন্য UI প্রজেক্টের .csproj ফাইলে InternalsVisibleTo কনফিগার করা হয়েছে, যাতে Test প্রজেক্ট UI-এর Program ক্লাস অ্যাক্সেস করতে পারে।
  • NuGet Packages: টেস্টিংয়ের জন্য Moq, AutoFixture, এবং FluentAssertions প্যাকেজগুলো ইন্সটল করা হয়েছে। Integration Test-এর জন্য অতিরিক্ত কিছু প্যাকেজ (Mvc.Testing, InMemory, HtmlAgilityPack) যুক্ত করা হয়েছে।
  • Project References: Test প্রজেক্টগুলো থেকে Core এবং UI প্রজেক্টের প্রয়োজনীয় রেফারেন্স অ্যাড করা হয়েছে।
  • Successful Execution: কোনো লজিক্যাল কোড পরিবর্তন ছাড়াই সবগুলো টেস্ট রান করানো হয়েছে এবং তা সফলভাবে পাস করেছে।

🔍 Comprehensive Breakdown

1. Why Separate Test Projects? (Priority: 9/10)

Why: পুরনো প্রজেক্টে আমাদের একটিমাত্র Test প্রজেক্ট ছিল যেখানে সব ধরনের টেস্ট রাখা হতো। কিন্তু Clean Architecture-এ Separation of Concerns খুব গুরুত্বপূর্ণ। তাই Service (Core), Controller (UI) এবং Integration-এর জন্য আলাদা আলাদা Test প্রজেক্ট তৈরি করা হয়েছে। এতে প্রোজেক্ট ম্যানেজ করা সহজ হয় এবং কোন লেয়ারে সমস্যা হচ্ছে তা দ্রুত বের করা যায়।

2. Creating the Test Projects (Priority: 8/10)

tests Solution ফোল্ডারের ভেতরে তিনটি নতুন xUnit Test Project তৈরি করা হয়েছে:

  1. ContactsManager.ServiceTests
  2. ContactsManager.ControllerTests
  3. ContactsManager.IntegrationTests

(Visual Studio Code Shortcut): টার্মিনালে নিচের কমান্ডগুলো দিয়ে সহজেই এই প্রজেক্টগুলো তৈরি করা যায়:

dotnet new xunit -n ContactsManager.ServiceTests
dotnet new xunit -n ContactsManager.ControllerTests
dotnet new xunit -n ContactsManager.IntegrationTests
 

প্রজেক্ট তৈরির পর ডিফল্টভাবে তৈরি হওয়া UnitTest1.cs ফাইলগুলো ডিলিট করে দেওয়া হয়েছে।

3. Migrating Test Files (Priority: 8/10)

পুরনো প্রজেক্ট থেকে ফাইলগুলো নতুন প্রজেক্টে আনা হয়েছে:

  • ServiceTests: CountriesServiceTest.cs এবং PersonsServiceTest.cs কপি করা হয়েছে।
  • ControllerTests: PersonsControllerTest.cs কপি করা হয়েছে।
  • IntegrationTests: CustomWebApplicationFactory.cs এবং PersonsControllerIntegrationTest.cs কপি করা হয়েছে।

4. The InternalsVisibleTo Configuration (Priority: 10/10)

Why: Integration Test রান করার জন্য আমাদের CustomWebApplicationFactory ব্যবহার করতে হয়, যা UI প্রজেক্টের Program ক্লাসকে কল করে ইন-মেমোরি সার্ভার তৈরি করে। কিন্তু Program ক্লাসটি সাধারণত Internal বা private থাকে। তাই UI প্রজেক্টকে বলে দিতে হয় যেন সে তার Internal ক্লাসগুলো IntegrationTests প্রজেক্টকে অ্যাক্সেস করতে দেয়।

  • Action: UI প্রজেক্টে ডাবল ক্লিক করে এর .csproj ফাইলে <InternalsVisibleTo> প্রপার্টি যুক্ত করা হয়েছে। (এর ইমপ্লিমেন্টেশন নিচে দেওয়া হলো)।

5. Installing NuGet Packages (Priority: 10/10)

সঠিকভাবে Test রান করার জন্য নির্দিষ্ট প্রজেক্টে প্যাকেজ ইন্সটল করা হয়েছে:

  • All Test Projects (Service, Controller, Integration):

  • FluentAssertions (সহজে Assertions লেখার জন্য)

  • AutoFixture (অটোমেটিক ডামি ডাটা জেনারেট করার জন্য)

  • Moq (ডিপেন্ডেন্সি বা রিপোজিটরি মক করার জন্য)

  • Only in IntegrationTests:

  • Microsoft.AspNetCore.Mvc.Testing (Integration Test-এর মূল প্যাকেজ)

  • Microsoft.EntityFrameworkCore.InMemory (টেস্টিংয়ের সময় আসল ডাটাবেসের বদলে RAM-এ ডাটাবেস তৈরির জন্য)

  • Fizzler.Systems.HtmlAgilityPack (HTML Response বডি পার্স এবং চেক করার জন্য)

6. Adding Project References (Priority: 10/10)

Why: Test প্রজেক্টগুলো নিজে থেকে কিছু চেনে না। তাদের বলে দিতে হয় তারা কোন প্রজেক্টকে টেস্ট করবে।

  • ServiceTests: এটি Core প্রজেক্টকে টেস্ট করবে, তাই Core-এর রেফারেন্স অ্যাড করা হয়েছে।
  • ControllerTests: এটি Controller-কে টেস্ট করবে যা UI প্রজেক্টে আছে, তাই UI-এর রেফারেন্স অ্যাড করা হয়েছে।
  • IntegrationTests: এটি পুরো অ্যাপ্লিকেশন ফ্লো টেস্ট করবে, তাই এখানেও UI-এর রেফারেন্স অ্যাড করা হয়েছে।

7. Fixing Errors and Running Tests (Priority: 7/10)

সব কনফিগারেশন শেষে সলিউশন বিল্ড করলে কিছু ছোট এরর আসে। যেমন: CountriesServiceTest-এ Microsoft.EntityFrameworkCore namespace-টি অপ্রয়োজনীয়ভাবে যুক্ত ছিল, যা রিমুভ করা হয়েছে। এরপর Test Explorer থেকে Run All ক্লিক করলে দেখা যায় আগের আর্কিটেকচারের সব লজিক Clean Architecture-এও নিখুঁতভাবে কাজ করছে!


💻 Code Implementation

1. Exposing Program Class via .csproj (UI Project): লেকচারে দেখানো পদ্ধতিতে UI প্রজেক্টের ContactsManager.UI.csproj ফাইলে নিচের কোডটি যুক্ত করতে হয়:

<ItemGroup>
  <InternalsVisibleTo Include="ContactsManager.IntegrationTests" />
</ItemGroup>
 

🌟 Best Practices & .NET 10 Updates

  • Test Naming Convention: Test মেথডের নাম সবসময় বর্ণনামূলক হওয়া উচিত। Best practice হলো MethodName_StateUnderTest_ExpectedBehavior প্যাটার্ন ফলো করা (যেমন: AddPerson_NullRequest_ReturnsArgumentNullException)।
  • Keep Mocking Simple: শুধু ততটুকুই মক (Mock) করুন, যতটুকু আপনার টেস্ট পাস করার জন্য প্রয়োজন। ওভার-মকিং টেস্টকে জটিল করে তোলে।

.NET 10 Update (Smarter way for Program.cs access): .NET 6/7 থেকে শুরু করে .NET 10 পর্যন্ত Top-level statements-এর কারণে Program.cs এ কোনো ক্লাস ডিক্লেয়ারেশন থাকে না। লেকচারে দেখানো .csproj-এ InternalsVisibleTo ব্যবহার করা একটু ঝামেলার। এর চেয়ে Smarter Update হলো, সরাসরি আপনার UI প্রজেক্টের Program.cs ফাইলের একদম নিচে এই এক লাইন কোড যুক্ত করে দেওয়া:

// Program.cs (At the very bottom)
// ... app.Run();
 
public partial class Program { } // Integration test-কে এই ক্লাসটি অ্যাক্সেস করার সুযোগ দেয়
 

এই এক লাইন কোড লিখলে আপনাকে আর .csproj ফাইল এডিট করতে হবে না, এবং Integration Test খুব সহজেই Program ক্লাসকে চিনে নিবে!