اختبار الوحدات (NUnit/xUnit)
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 67 من 70.
التحدي
سهلدعنا نكمل مشروع نظام المكتبة الخاص بك عن طريق إضافة اختبارات الوحدة! يعد الاختبار أمراً ضرورياً لضمان عمل الكود الخاص بك بشكل صحيح واستمراره في العمل أثناء إجراء التغييرات. ستقوم بإنشاء ملف اختبار مخصص يتحقق من الوظائف الأساسية لنظام المكتبة الخاص بك.
ستستمر في العمل مع هيكل مشروعك الحالي، مع إضافة ملف اختبار جديد:
Book.cs: احتفظ بفئة Book الخاصة بك معId، وTitle، وAuthor، وIsAvailable، وطرقBorrow()وReturn().User.cs: احتفظ بفئة User الخاصة بك مع تتبع الاستعارة، وثابتMaxBooks، وCanBorrow()، وطرق إدارة الكتب المستعارة.Library.cs: احتفظ بفئة Library الخاصة بك مع منطق الاستعارة، وطرق البحث، وطرقGetAllBooks()وGetAllUsers().LibraryTests.cs: هذا هو ملف الاختبار الجديد الخاص بك! قم بإنشاء فئة اختبار تتحقق من أن نظام المكتبة الخاص بك يعمل بشكل صحيح. ستكتب طرق اختبار تتحقق من سيناريوهات مختلفة وتبلغ عما إذا كان كل اختبار قد نجح أم فشل.Program.cs: نقطة الدخول الخاصة بك التي تقوم بتشغيل الاختبارات وعرض النتائج.
يجب أن تتضمن فئة الاختبار الخاصة بك طرقاً تختبر هذه السيناريوهات:
TestBookBorrow- التحقق من أن استعارة كتاب متاح تنجح وتغير حالة توفرهTestBookBorrowUnavailable- التحقق من أن استعارة كتاب مستعار بالفعل تفشل بشكل مناسبTestUserBorrowingLimit- التحقق من أن المستخدمين لا يمكنهم استعارة أكثر من 3 كتبTestBookReturn- التحقق من أن إعادة كتاب مستعار تعمل بشكل صحيحTestSearchByAuthor- التحقق من أن البحث حسب المؤلف يعيد الكتب الصحيحة
يجب أن تعيد كل طريقة اختبار قيمة منطقية (boolean) تشير إلى ما إذا كان الاختبار قد نجح. قم بإنشاء مشغل اختبار بسيط ينفذ جميع الاختبارات ويطبع النتائج.
ستتلقى المدخلات التالية:
- عدد سيناريوهات الاختبار المراد تشغيلها (1-5، المقابلة للاختبارات المدرجة أعلاه)
لكل اختبار يتم تشغيله، اطبع النتيجة بالتنسيق التالي:
{TestName}: {PASS|FAIL}بعد اكتمال جميع الاختبارات، اطبع ملخصاً:
--- Test Summary ---
Passed: {count}
Failed: {count}
Total: {count}على سبيل المثال، إذا كان الإدخال هو:
3يجب أن يكون المخرج:
TestBookBorrow: PASS
TestBookBorrowUnavailable: PASS
TestUserBorrowingLimit: PASS
--- Test Summary ---
Passed: 3
Failed: 0
Total: 3مثال آخر مع جميع الاختبارات الخمسة:
5يجب أن يكون المخرج:
TestBookBorrow: PASS
TestBookBorrowUnavailable: PASS
TestUserBorrowingLimit: PASS
TestBookReturn: PASS
TestSearchByAuthor: PASS
--- Test Summary ---
Passed: 5
Failed: 0
Total: 5يجب أن تنشئ اختباراتك مثيلات جديدة من Book و User و Library لكل اختبار لضمان العزل. بهذه الطريقة، لا تؤثر حالة أحد الاختبارات على اختبار آخر. فكر في كل اختبار كتحقق ذاتي من سلوك معين في نظامك!
جرّب بنفسك
using System;
using System.Collections.Generic;
namespace LibrarySystem
{
class Program
{
public static void Main(string[] args)
{
// Read library name
string libraryName = Console.ReadLine();
// Create a Library instance with the library name
Library library = new Library(libraryName);
// Read number of commands
int numCommands = Convert.ToInt32(Console.ReadLine());
// Process commands
for (int i = 0; i < numCommands; i++)
{
string commandLine = Console.ReadLine();
string[] parts = commandLine.Split('|');
string command = parts[0];
if (command == "ADD_BOOK")
{
string bookId = parts[1];
string bookTitle = parts[2];
string bookAuthor = parts[3];
Book book = new Book(bookId, bookTitle, bookAuthor);
library.AddBook(book);
Console.WriteLine($"Added: {bookTitle}");
}
else if (command == "ADD_USER")
{
string userName = parts[1];
int memberId = Convert.ToInt32(parts[2]);
User user = new User(userName, memberId);
library.RegisterUser(user);
Console.WriteLine($"Registered: {userName}");
}
else if (command == "BORROW")
{
string bookId = parts[1];
int memberId = Convert.ToInt32(parts[2]);
string result = library.BorrowBook(bookId, memberId);
Console.WriteLine(result);
}
else if (command == "RETURN")
{
string bookId = parts[1];
int memberId = Convert.ToInt32(parts[2]);
string result = library.ReturnBook(bookId, memberId);
Console.WriteLine(result);
}
else if (command == "SEARCH")
{
string searchType = parts[1];
List<Book> results = new List<Book>();
if (searchType == "title")
{
string searchValue = parts[2];
results = library.SearchByTitle(searchValue);
}
else if (searchType == "author")
{
string searchValue = parts[2];
results = library.GetBooksByAuthor(searchValue);
}
else if (searchType == "available")
{
results = library.GetAvailableBooks();
}
else if (searchType == "borrowed")
{
results = library.GetBorrowedBooks();
}
if (results.Count == 0)
{
Console.WriteLine("No books found");
}
else
{
foreach (Book book in results)
{
Console.WriteLine($"{book.Title} by {book.Author}");
}
}
}
else if (command == "STATUS")
{
List<Book> allBooks = library.GetAllBooks();
List<Book> availableBooks = library.GetAvailableBooks();
List<User> allUsers = library.GetAllUsers();
List<Book> borrowedBooks = library.GetBorrowedBooks();
Console.WriteLine("--- Library Status ---");
Console.WriteLine($"Total Books: {allBooks.Count}");
Console.WriteLine($"Available: {availableBooks.Count}");
Console.WriteLine($"Total Users: {allUsers.Count}");
Console.WriteLine($"Books Borrowed: {borrowedBooks.Count}");
}
}
}
}
}
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injection3بنية الكلاسات
بيانات الـ Instance مقابل البيانات الـ Staticالكلمات المفتاحية 'readonly' و 'const'الحقول الخلفية (Backing Fields)مراجعة - مدير الحساب البنكي6التغليف
معدلات الوصولالخصائص من أجل التغليفتنفيذ إخفاء البياناتأنماط عدم القابلية للتغييرمراجعة - سجلات الطلاب12مشروع: نظام المكتبة
هيكلة المشروعنماذج الكتاب والمستخدم