হ্যালো হাসিব! আজকের লেকচারটি Integration Testing-এর একটি চমৎকার এবং প্র্যাকটিক্যাল ইউজ-কেস নিয়ে, যেখানে আমরা দেখব কীভাবে HTTP Response থেকে সরাসরি HTML DOM টেস্ট করা যায়। চলো কথা না বাড়িয়ে সোজা মূল টপিকে ঢুকে যাই।
📝 Short Summary for Quick Revision
- xUnit Limitation: xUnit-এ বাই-ডিফল্ট HTML DOM টেস্ট করার কোনো বিল্ট-ইন ফিচার নেই।
- Required Packages: এই সীমাবদ্ধতা দূর করতে থার্ড-পার্টি টুল Fizzler এবং HtmlAgilityPack ব্যবহার করা হয়।
- Reading Response: Integration Test-এ HTTP Response-এর বডি থেকে প্লেন স্ট্রিং হিসেবে HTML রিড করতে
ReadAsStringAsync()ব্যবহার করা হয়। - Parsing DOM:
HtmlDocumentক্লাসের মাধ্যমে স্ট্রিং থেকে একটি ভ্যালিড HTML DOM তৈরি করা হয়। - Querying Elements: JavaScript-এর মতো
QuerySelectorAllমেথড ব্যবহার করে নির্দিষ্ট CSS Class (যেমন:table.persons) দিয়ে কাঙ্ক্ষিত HTML Element খুঁজে বের করে Assert করা হয়। - Testing Philosophy: Unit Testing হলো White-box testing (ভেতরের কোড চেক করে), আর Integration Testing হলো Black-box testing (শুধু Request এবং Response চেক করে)।
🔍 Comprehensive Breakdown
১. HTML DOM Testing এর প্রয়োজনীয়তা এবং প্যাকেজ ইন্সটলেশন [Importance: 8/10]
যখন আমরা MVC প্যাটার্নে কাজ করি, Controller সাধারণত View (HTML) রিটার্ন করে। Integration Test করার সময় আমরা চাইলেই নিশ্চিত হতে পারি যে রেসপন্সে নির্দিষ্ট কোনো বাটন, টেবিল বা এরর মেসেজ রেন্ডার হয়েছে কিনা। কিন্তু xUnit-এর নিজস্ব কোনো HTML পার্সার নেই। তাই লেকচারার এখানে দুটি প্যাকেজ ইন্সটল করার কথা বলেছেন।
ভিডিওতে Visual Studio-এর UI ব্যবহার করে NuGet Package ইন্সটল দেখানো হয়েছে। তুমি যদি VS Code ব্যবহার করো, তবে টার্মিনালে নিচের কমান্ডগুলো রান করে সহজেই প্যাকেজগুলো ইন্সটল করতে পারবে:
# VS Code (CLI Command)
dotnet add package Fizzler --version 1.3.0
dotnet add package Fizzler.Systems.HtmlAgilityPack --version 1.2.1
২. HTTP Response Body রিড করা [Importance: 10/10]
Integration Test-এ যখন আমরা কোনো এন্ডপয়েন্টে (যেমন: /persons/index) GET Request পাঠাই, তখন অ্যাপ্লিকেশনটির সব লেয়ার (Controller > Service > Repository) এক্সিকিউট হয়ে ফাইনালি একটি HTML Response তৈরি হয়।
এই Response Object-এর ভেতরে Headers, Body এবং Status Code থাকে। আমাদের কাজ হলো Body থেকে প্লেন HTML কোডটি রিড করা।
// Making the GET request
HttpResponseMessage response = await _client.GetAsync("/persons/index");
// Reading the HTML body as a plain string
string responseString = await response.Content.ReadAsStringAsync();
৩. HTML DOM জেনারেট করা [Importance: 9/10]
যেহেতু responseString কেবল একটি সাধারণ C# স্ট্রিং, তাই আমরা এটি থেকে সরাসরি HTML ট্যাগ বা অ্যাট্রিবিউট খুঁজতে পারব না। এখানেই HtmlAgilityPack কাজে আসে। এটি ওই স্ট্রিংটিকে একটি স্ট্রাকচারড HTML DOM-এ রূপান্তর করে।
using HtmlAgilityPack; // Import namespace
// Create a new empty HTML Document
var document = new HtmlDocument();
// Load the plain HTML string into the document
document.LoadHtml(responseString);
৪. CSS Selector দিয়ে Element খোঁজা এবং Assert করা [Importance: 10/10]
JavaScript-এ আমরা যেমন document.querySelectorAll() ব্যবহার করে DOM ম্যানিপুলেট করি, Fizzler ঠিক একই সুবিধা C#-এ নিয়ে আসে। লেকচারার দেখিয়েছেন, View (HTML) ফাইলে টেবিলের একটি নির্দিষ্ট CSS Class (যেমন: class="persons") দিয়ে দিলে, টেস্ট ফাইলে খুব সহজেই সেটি খুঁজে বের করা যায়।
using Fizzler.Systems.HtmlAgilityPack; // Import namespace for QuerySelectorAll
// Assuming the HTML has: <table class="persons">...</table>
var tableNode = document.DocumentNode.QuerySelectorAll("table.persons").FirstOrDefault();
// Assertion: Make sure the table actually exists in the rendered HTML
Assert.NotNull(tableNode);
// If you are using FluentAssertions:
// tableNode.Should().NotBeNull();
কেন এটি জরুরি? এর মাধ্যমে তুমি নিশ্চিত হতে পারো যে তোমার অ্যাপ্লিকেশন শুধু 200 OK স্ট্যাটাসই দিচ্ছে না, বরং সঠিক UI এলিমেন্টগুলোও রেন্ডার করছে।
৫. Unit Testing বনাম Integration Testing [Importance: 10/10]
লেকচারার এই দুই ধরনের টেস্টিংয়ের মূল পার্থক্য খুব সুন্দরভাবে বুঝিয়ে বলেছেন:
- Unit Testing (White-box Testing): এখানে তুমি ঠিকই জানো ভেতরে কী হচ্ছে। তুমি একটি নির্দিষ্ট Controller বা Service মেথডকে আলাদা করে (Isolate) টেস্ট করো। একটি মেথডের জন্য অন্তত দুটি টেস্ট লেখা উচিত (Valid Input এবং Invalid Input)।
- Integration Testing (Black-box Testing): এখানে তোমার দেখার বিষয় নয় ভেতরে কোন মেথড কল হলো বা ডেটাবেস কীভাবে কাজ করল। তুমি শুধু একটি HTTP Request পাঠাবে এবং চেক করবে কাঙ্ক্ষিত Response পেয়েছ কিনা। প্রতিটি Route বা URL-এর জন্য অন্তত একটি Integration Test থাকা উচিত।
💡 Best Practices & Modern Updates (.NET 10)
Best Practices for HTML DOM Testing:
- Avoid Tight Coupling: পুরো HTML পেজের টেক্সট বা ডিজাইন হুবহু টেস্ট করবে না। শুধু গুরুত্বপূর্ণ Data Attributes, ID বা নির্দিষ্ট CSS Class টেস্ট করবে (যেমন:
<div id="error-message">)। ডিজাইন পরিবর্তন হলে যেন টেস্ট ফেইল না করে। - Semantic Selectors: ক্লাস নেমের চেয়ে
data-test-idঅ্যাট্রিবিউট ব্যবহার করা আরও ভালো প্র্যাকটিস। (যেমন:<table data-testid="persons-table">)।
🚀 The Smarter & Updated Way (.NET 10):
লেকচারে দেখানো HtmlAgilityPack + Fizzler বেশ পুরোনো পদ্ধতি। আধুনিক .NET অ্যাপ্লিকেশনে (বিশেষ করে .NET 8, 9 বা 10-এ) HTML DOM পার্সিং এবং টেস্টিংয়ের জন্য AngleSharp সবচেয়ে জনপ্রিয় এবং ফাস্ট লাইব্রেরি। এটি সরাসরি W3C স্ট্যান্ডার্ড মেইনটেইন করে এবং কোনো এক্সট্রা প্যাকেজ (যেমন Fizzler) ছাড়াই চমৎকার Query Selector সাপোর্ট দেয়।
AngleSharp ব্যবহার করে আধুনিক ইমপ্লিমেন্টেশন:
# Install modern DOM parser
dotnet add package AngleSharp
using AngleSharp.Html.Parser;
using AngleSharp.Dom;
// 1. Read response body
string responseString = await response.Content.ReadAsStringAsync();
// 2. Setup AngleSharp Parser
var parser = new HtmlParser();
using IHtmlDocument document = await parser.ParseDocumentAsync(responseString);
// 3. Query the DOM using standard CSS selectors
var tableElement = document.QuerySelector("table.persons");
// 4. Assert
Assert.NotNull(tableElement);
// You can even check text content easily:
// Assert.Equal("John Doe", tableElement.QuerySelector(".name-cell").TextContent);
কেন AngleSharp ভালো? এটি পুরোপুরি Asynchronous সাপোর্ট করে, মেমরি ম্যানেজমেন্টে অনেক বেশি ইফিশিয়েন্ট এবং ব্রাউজারের একদম কাছাকাছি পারফরম্যান্স দেয়।
আশা করি লেকচারের প্রতিটি কনসেপ্ট এখন তোমার কাছে একদম পরিষ্কার! প্র্যাকটিস করার সময় কোনো জায়গায় আটকে গেলে নির্দ্বিধায় জানাবে।