হ্যালো হাসিব! আমরা আজকে টেস্টিংয়ের আরেকটি খুবই দারুণ এবং পপুলার লাইব্রেরি নিয়ে কথা বলবো—FluentAssertions

ইউনিট টেস্টের শেষ ধাপে আমরা Assert ব্যবহার করে রেজাল্ট চেক করি। কিন্তু ডিফল্ট Assert.Equal(expected, actual) অনেক সময় মানুষের পড়ার জন্য (Readability) একটু কঠিন বা আন-ন্যাচারাল মনে হতে পারে। FluentAssertions ঠিক এই সমস্যাটাই সমাধান করে। এটি কোডকে অনেকটা সাধারণ ইংরেজি বাক্যের মতো করে তোলে।

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

📝 Lecture Summary at a Glance

  • The Concept: FluentAssertions হলো একটি লাইব্রেরি যা টেস্টের Assert লজিককে মানুষের স্বাভাবিক কথাবার্তার (Natural Language) মতো করে লিখতে সাহায্য করে।
  • The Syntax Rule: ডিফল্ট Assert-এ আমরা আগে Expected এবং পরে Actual ভ্যালু লিখি। কিন্তু FluentAssertions-এ সবসময় Actual ভ্যালু আগে লিখতে হয় এবং তারপর .Should() চেইন করতে হয়। (যেমন: actual.Should().Be(expected))
  • Exception Testing: Exception টেস্ট করার জন্য ল্যাম্বডা এক্সপ্রেশনকে একটি Func<Task> ডেলিগেটে নিয়ে তারপর .Should().ThrowAsync<TException>() ব্যবহার করা হয়।
  • Collection Testing: লিস্ট বা কালেকশন টেস্ট করার জন্য .Should().Contain() বা .Should().BeEquivalentTo() এর মতো বিল্ট-ইন পাওয়ারফুল মেথড আছে।

🧠 Comprehensive Breakdown & Deep Dive

১. Setting up FluentAssertions [Importance: 9/10]

  • The “Why”: তোমার টেস্ট কোড যদি অন্য কোনো ডেভেলপার বা নন-টেকনিক্যাল পার্সন (যেমন QA ইঞ্জিনিয়ার) পড়ে, সে যেন খুব সহজেই বুঝতে পারে টেস্টটি কী চেক করছে।

💻 Package Installation: টেস্ট প্রজেক্টের টার্মিনালে কমান্ড দিয়ে প্যাকেজটি ইনস্টল করতে হবে:

dotnet add package FluentAssertions
 

এরপর তোমার টেস্ট ক্লাসের ওপরে using FluentAssertions; অ্যাড করে নিতে হবে।

২. Basic Assertions (Be, NotBe, BeNull) [Importance: 10/10]

  • The “Why”: এটি সবচেয়ে বেসিক এবং বেশি ব্যবহৃত ফিচার।

💻 Code Implementation:

// ❌ Old Way (xUnit Assert):
// Assert.NotEqual(Guid.Empty, personResponse.PersonId);
// Assert.Null(personResponse);
 
// ✅ Modern Fluent Way:
personResponse.PersonId.Should().NotBe(Guid.Empty);
personResponse.Should().BeNull();
 

(পড়তে কতটা সহজ দেখো: “personResponse should be null”)

৩. Exception Testing (The Func Trick) [Importance: 10/10]

  • The “Why”: অ্যাসিংক্রোনাস এক্সেপশন টেস্ট করা xUnit-এ একটু প্যাঁচানো। FluentAssertions এটাকে অনেক সুন্দর করে দেয়। তবে এর জন্য সরাসরি ল্যাম্বডা পাস না করে, ল্যাম্বডাকে একটি ভেরিয়েবলে (Func<Task>) রাখতে হয়।

💻 Code Implementation:

// ❌ Old Way:
/*
await Assert.ThrowsAsync<ArgumentNullException>(async () =>
{
    await _personService.AddPerson(null);
});
*/
 
// ✅ Fluent Way:
Func<Task> action = async () =>
{
    await _personService.AddPerson(null);
};
 
// "This action should throw an ArgumentNullException"
await action.Should().ThrowAsync<ArgumentNullException>();
 

৪. Collection/List Testing (BeEquivalentTo) [Importance: 10/10]

  • The “Why”: লেকচারার সবচেয়ে পাওয়ারফুল যে মেথডটি দেখিয়েছেন তা হলো BeEquivalentTo। xUnit-এ দুটি লিস্টের ভেতরের ডাটা সেম কিনা তা চেক করতে foreach লুপ চালিয়ে একটা একটা করে চেক করতে হতো। FluentAssertions-এ এটি জাস্ট এক লাইনের কাজ!

💻 Code Implementation:

List<PersonResponse> actualList = await _personService.GetAllPersons();
List<PersonResponse> expectedList = /* ... তোমার বানানো লিস্ট ... */;
 
// ❌ Old Way (Looping):
/*
foreach (var expected in expectedList)
{
    Assert.Contains(expected, actualList);
}
*/
 
// ✅ Super Fluent Way:
actualList.Should().BeEquivalentTo(expectedList);
 

(এই BeEquivalentTo ইন্টারনালি রিফ্লেকশন ব্যবহার করে দুটি লিস্টের প্রতিটি অবজেক্টের প্রতিটি প্রপার্টি ম্যাচ করে দেখে। এটি ম্যাজিকের মতো কাজ করে!)


🚀 Modern Testing Best Practices (FluentAssertions)

হাসিব, FluentAssertions-এ আরও কিছু অ্যাডভান্সড ফিচার আছে যা তোমার প্রজেক্টে অনেক সময় বাঁচাবে:

১. Date and Time Assertions: ডেট নিয়ে কাজ করা অনেক সময় পেইনফুল হয় (যেমন একটু মিলিসেকেন্ড এদিক-ওদিক হলে টেস্ট ফেইল করে)। FluentAssertions-এ তুমি টলারেন্স (Tolerance) সেট করে দিতে পারো:

person.DateOfBirth.Should().BeCloseTo(expectedDate, TimeSpan.FromSeconds(1));
 

২. String Assertions: সার্চ বা ফিল্টার টেস্ট করার সময় এটি খুব কাজে লাগে:

person.Name.Should().StartWith("Ha").And.EndWith("ib").And.Contain("s");
 

৩. Custom Error Messages: যেকোনো Should() মেথডের ভেতরে তুমি কাস্টম এরর মেসেজ দিয়ে দিতে পারো, যাতে টেস্ট ফেইল হলে তুমি বুঝতে পারো কেন ফেইল হলো:

personList.Should().NotBeEmpty("because we just added 3 persons to the database");
 

পরবর্তী লেকচারে লেকচারার PersonsServiceTest ক্লাসের বাকি টেস্টগুলোকেও FluentAssertions-এ কনভার্ট করবেন। তুমি চাইলে পরের ট্রান্সক্রিপ্টটি দিতে পারো!