হ্যালো হাসিব! আজকের লেকচারটি টেস্টিংয়ের জগতে খুবই গুরুত্বপূর্ণ একটি কনসেপ্ট নিয়ে—Mocking

আগের লেকচারগুলোতে তুমি খেয়াল করেছো, যখন আমরা আসল ডাটাবেস (SQL Server) কানেক্ট করে টেস্ট রান করেছিলাম, তখন টেস্টগুলো ফেইল করছিল। কারণ ইউনিট টেস্টের গোল্ডেন রুল হলো: “Unit tests should be isolated and fast. They must never depend on an external database or network.” তাই রিয়েল ডাটাবেসের বদলে একটি “ফেক” বা “মক” ডাটাবেস কীভাবে তৈরি করতে হয়, সেটাই আমরা এই লেকচারে শিখবো।

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

📝 Lecture Summary at a Glance

  • The Problem: Unit Test-এ রিয়েল SQL Server ডাটাবেস ব্যবহার করা যায় না। কারণ এতে টেস্ট স্লো হয়ে যায় এবং ডাটাবেস নোংরা (Garbage Data) হয়ে যায়।

  • Test Double (Mock vs Fake): * Fake: একটি ক্লাসের সম্পূর্ণ ডামি ইমপ্লিমেন্টেশন (যেমন EF Core In-Memory Database)।

  • Mock: একটি ক্লাসের আংশিক বা নির্দিষ্ট কোনো মেথডের ডামি ইমপ্লিমেন্টেশন।

  • The Tools: Moq (মোস্ট পপুলার মকিং ফ্রেমওয়ার্ক) এবং EntityFrameworkCoreMock.Moq (সহজে DbContext মক করার জন্য)।

  • The virtual Keyword: DbContext-এর DbSet প্রপার্টিগুলোকে অবশ্যই virtual হতে হবে, না হলে Moq সেগুলোকে মক (Override) করতে পারবে না।


🧠 Comprehensive Breakdown & Deep Dive

১. Mocking Setup in xUnit [Importance: 9/10]

  • The “Why”: আমরা চাই আমাদের সার্ভিস (যেমন CountriesService) ভাবুক যে সে আসল ডাটাবেসের সাথেই কথা বলছে, কিন্তু আসলে সে একটি ডামি লিস্ট (In-Memory Collection) এর সাথে কথা বলবে।

💻 Package Installation: টার্মিনালে তোমার টেস্ট প্রজেক্টের (যেমন CrudTests) ডিরেক্টরিতে গিয়ে নিচের কমান্ডগুলো রান করো:

dotnet add package Moq
dotnet add package EntityFrameworkCoreMock.Moq
 

২. Mocking DbContext and DbSet [Importance: 10/10]

  • The “Why”: ApplicationDbContext (লেকচারার এর নাম পরিবর্তন করেছেন) সরাসরি ডাটাবেসে কানেক্ট করে। আমরা DbContextMock ক্লাস ব্যবহার করে এর একটি ক্লোন (Mock) তৈরি করবো, যা রিয়েল ডাটাবেসের বদলে আমাদের দেওয়া একটি ফাঁকা List<Country> ব্যবহার করবে।

💻 Code Implementation (CountriesServiceTest.cs):

using EntityFrameworkCoreMock;
using Moq;
 
public class CountriesServiceTest
{
    private readonly ICountriesService _countriesService;
 
    public CountriesServiceTest()
    {
        // ১. ডাটাবেসের বিকল্প হিসেবে একটি ফাঁকা লিস্ট তৈরি করা
        var countriesInitialData = new List<Country>();
 
        // ২. DbContext-এর Mock অবজেক্ট তৈরি করা
        var dbContextOptions = new DbContextOptionsBuilder<ApplicationDbContext>().Options;
        var dbContextMock = new DbContextMock<ApplicationDbContext>(dbContextOptions);
 
        // ৩. DbSet-কে Mock করা (যাতে এটি আমাদের বানানো লিস্ট ব্যবহার করে)
        dbContextMock.CreateDbSetMock(x => x.Countries, countriesInitialData);
 
        // ৪. Mock অবজেক্টকে সার্ভিসে ইনজেক্ট করা
        var dbContext = dbContextMock.Object; 
        _countriesService = new CountriesService(dbContext);
    }
}
 

(এই সেটআপের পর _countriesService.AddCountry কল করলে ডাটাবেসে সেভ না হয়ে, আমাদের ডামি countriesInitialData লিস্টে সেভ হবে!)

৩. The virtual Keyword Error [Importance: 10/10]

  • The “Why”: মকিং সেটআপ করার পর যখন টেস্ট রান করা হয়, তখন NotSupportedException আসে। এর কারণ হলো, Moq ফ্রেমওয়ার্ক ইন্টারনালি তোমার ক্লাসের একটি চাইল্ড ক্লাস তৈরি করে মেথডগুলোকে ওভাররাইড (Override) করে। C#-এ কোনো প্রপার্টি বা মেথড ওভাররাইড করতে চাইলে সেটিকে অবশ্যই virtual হতে হয়।

💻 Fix in ApplicationDbContext.cs:

public class ApplicationDbContext : DbContext
{
    // ❌ Old Way:
    // public DbSet<Country> Countries { get; set; }
    
    // ✅ Fix for Mocking:
    public virtual DbSet<Country> Countries { get; set; }
    public virtual DbSet<Person> Persons { get; set; }
}
 

(এটি করার পর সব টেস্ট ১০০% পাস করবে!)


🚀 Modern Architecture Notes & Best Practices

The Reality of EntityFrameworkCoreMock: হাসিব, লেকচারার যে প্যাকেজটি (EntityFrameworkCoreMock.Moq) ব্যবহার করেছেন, তা ছোট প্রজেক্টের জন্য ঠিক আছে, কিন্তু এটি থার্ড-পার্টি প্যাকেজ এবং অনেক সময় EF Core-এর নতুন আপডেট (যেমন .NET 8/10) এর সাথে কাজ করে না।

ইন্ডাস্ট্রিতে বর্তমানে DbContext মক করাকে একটি “Anti-Pattern” বা ব্যাড প্র্যাকটিস ধরা হয়। এর প্রধান দুটি কারণ: ১. EF Core নিজেই অনেক বড় এবং জটিল। এর সবকিছু (Include, ThenInclude, AsNoTracking) মক করা প্রায় অসম্ভব। ২. মক করা ডাটাবেস রিয়েল ডাটাবেসের মতো কাজ করে না (যেমন Foreign Key constraint চেক করে না)।

✅ The Clean Architecture Solution: ১. In-Memory Database (For quick tests): মাইক্রোসফটের অফিশিয়াল Microsoft.EntityFrameworkCore.InMemory প্যাকেজ ব্যবহার করা, যা কোনো মকিং ফ্রেমওয়ার্ক ছাড়াই ডামি ডাটাবেস তৈরি করে দেয়। ২. Repository Pattern (The Ultimate Solution): লেকচারার ভিডিওর শেষে বলেছেন, রিয়েল-ওয়ার্ল্ড আর্কিটেক্টরা DbContext-কে মক করেন না। তারা Repository Pattern ব্যবহার করেন এবং শুধু IRepository-কে Moq দিয়ে মক করেন। এটি অনেক বেশি ক্লিন এবং সেফ।

পরবর্তী লেকচারগুলোতে সম্ভবত AutoFixture বা FluentAssertions নিয়ে আলোচনা হবে এবং তারপরই আমরা Repository Pattern-এ প্রবেশ করবো। তুমি রেডি হলে পরের ট্রান্সক্রিপ্টটি দিতে পারো!