হ্যালো হাসিব! এই লেকচারটিতে আমরা ইউনিট টেস্টিংয়ের সবচেয়ে জাদুকরী এবং রিয়েল-ওয়ার্ল্ড পার্ট—True Mocking নিয়ে আলোচনা করবো।

আগের লেকচারগুলোতে আমরা DbContext-কে মক করেছিলাম, যা বেশ ঝামেলার এবং ক্লিন আর্কিটেকচারের দিক থেকে খুব একটা ভালো প্র্যাকটিস ছিল না। কিন্তু এখন যেহেতু আমাদের আর্কিটেকচারে Repository Pattern চলে এসেছে, তাই টেস্টিং আগের চেয়ে ১০০ গুণ সহজ হয়ে গেছে। আমরা সরাসরি Moq লাইব্রেরি ব্যবহার করে Repository-কেই মক করে ফেলবো।

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

📝 Lecture Summary at a Glance

  • The Goal: PersonsService-কে টেস্ট করার সময় ডাটাবেসের ওপর (অর্থাৎ PersonsRepository এর ওপর) থেকে সম্পূর্ণ ডিপেন্ডেন্সি সরিয়ে ফেলা।
  • The Moq Library: Moq প্যাকেজ ব্যবহার করে IPersonsRepository-এর একটি ফেক বা ডামি ইমপ্লিমেন্টেশন তৈরি করা হয়েছে।
  • Setup & Returns: Setup() মেথড দিয়ে বলে দেওয়া হয়েছে যে, সার্ভিসের ভেতর থেকে রিপোজিটরির অমুক মেথড কল হলে, ডাটাবেসে না গিয়ে সরাসরি একটি ডামি ডাটা রিটার্ন করতে হবে।
  • The Magic of .Object: Mock<IPersonsRepository> এর .Object প্রপার্টিটি কল করলেই ইন্টারফেসের একটি রেডিমেড ডামি ক্লাস তৈরি হয়ে যায়, যা সার্ভিসের কনস্ট্রাক্টরে ইনজেক্ট করা যায়।

🧠 Comprehensive Breakdown & Deep Dive

১. Why Mock the Repository? [Importance: 10/10]

  • The “Why”: ইউনিট টেস্টের প্রধান শর্ত হলো এটি “Isolated” হতে হবে। PersonsService টেস্ট করার সময় আমরা যদি আসল রিপোজিটরি ব্যবহার করি, তবে সেটি ডাটাবেসে ডাটা ইনসার্ট করে দেবে। ডাটাবেস ছাড়া টেস্ট রান করলে তা ক্র্যাশ করবে। তাই আমরা রিপোজিটরিকে মক করি, যাতে সার্ভিস ভাবে সে ডাটাবেসে ডাটা সেভ করছে, কিন্তু আসলে সে একটি ডামি অবজেক্টের সাথে খেলছে!

২. Creating the Mock Object [Importance: 9/10]

  • The “Why”: আমরা new PersonsRepository() তৈরি না করে Moq কে বলি আমাদের জন্য একটি ফেক রিপোজিটরি বানিয়ে দিতে।

💻 Code Implementation:

using Moq;
using RepositoryContracts;
 
public class PersonsServiceTest
{
    private readonly IPersonsService _personsService;
    private readonly Mock<IPersonsRepository> _personRepositoryMock; // Mock Object
    
    public PersonsServiceTest()
    {
        // ১. Mock অবজেক্ট ইনিশিয়ালাইজ করা
        _personRepositoryMock = new Mock<IPersonsRepository>();
        
        // ২. Mock এর ফেক ইমপ্লিমেন্টেশনটা (Object) সার্ভিসে ইনজেক্ট করা
        _personsService = new PersonsService(_personRepositoryMock.Object);
    }
}
 

৩. The “Setup” and “Returns” Magic [Importance: 10/10]

  • The “Why”: ফেক রিপোজিটরি তো বানালাম, কিন্তু সে তো আর নিজে থেকে জানে না AddPerson কল হলে কী রিটার্ন করতে হবে! তাই টেস্ট মেথডের ভেতরে (Arrange সেকশনে) আমাদের এটা ম্যানুয়ালি সেটআপ করে দিতে হয়।
  • It.IsAny<T>(): এর মানে হলো, “যদি কেউ AddPerson মেথডে Person টাইপের যেকোনো ডাটা প্যারামিটার হিসেবে পাঠায়, তবে তুমি এই নির্দিষ্ট রিটার্ন ভ্যালুটা দিয়ে দেবে।”

💻 Code Implementation (AddPerson Test):

[Fact]
public async Task AddPerson_FullPersonDetails_ToBeSuccessful()
{
    // Arrange: ডামি ডাটা তৈরি করা (AutoFixture দিয়ে)
    PersonAddRequest personAddRequest = _fixture.Create<PersonAddRequest>();
    Person person = personAddRequest.ToPerson();
    PersonResponse personResponseExpected = person.ToPersonResponse();
 
    // The Mocking Magic: 
    // যখনই রিপোজিটরির AddPerson কল হবে যেকোনো Person অবজেক্ট দিয়ে...
    _personRepositoryMock
        .Setup(temp => temp.AddPerson(It.IsAny<Person>())) 
        .ReturnsAsync(person); // ...তখন ডাটাবেসে না গিয়ে এই ডামি person টা রিটার্ন করে দেবে!
 
    // Act: আসল সার্ভিস মেথড কল করা
    PersonResponse personResponseFromAdd = await _personsService.AddPerson(personAddRequest);
    
    // Assert: চেক করা যে সার্ভিস ঠিকমতো কাজ করেছে কিনা
    personResponseExpected.PersonId = personResponseFromAdd.PersonId;
    personResponseFromAdd.Should().BeEquivalentTo(personResponseExpected);
}
 

🚀 Modern Clean Architecture Pro Tips

তোমার “Chatrabash” প্রোজেক্টের ইউনিট টেস্টগুলো লেখার সময় এই প্যাটার্নটি সবচেয়ে বেশি কাজে লাগবে। কারণ SaaS অ্যাপ্লিকেশনে ডাটাবেস কল অনেক এক্সপেন্সিভ হয়।

একটি স্পেশাল প্রো-টিপ: লেকচারার বলেছেন যে, একটি টেস্টে একটির বেশি সার্ভিস মেথড কল করা উচিত নয়। আগের ভার্সনে AddPerson টেস্ট করার পর সে আসলেই ডাটাবেসে অ্যাড হয়েছে কিনা তা ভেরিফাই করার জন্য আমরা GetAllPersons কল করতাম। কিন্তু মকিংয়ের যুগে এর কোনো দরকার নেই! কারণ আমরা তো ডাটাবেস টেস্ট করছি না, আমরা টেস্ট করছি PersonsService-এর বিজনেস লজিক ঠিকমতো কাজ করে কি না।

লেকচারার অ্যাসাইনমেন্ট দিয়েছেন বাকি টেস্টগুলোতে মক অ্যাপ্লাই করার জন্য। তুমি কি চাও আমি বাকি মেথডগুলোর (যেমন Get, Update, Delete) মকিং সেটআপ কীভাবে করতে হয়, তার একটি ডেমো কোড লিখে দিই?