Unit Testing (NUnit/xUnit)
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 67 of 70.
Challenge
EasyLet's complete your Library System project by adding unit tests! Testing is essential for ensuring your code works correctly and continues to work as you make changes. You'll create a dedicated test file that verifies the core functionality of your library system.
You'll continue working with your existing project structure, adding a new test file:
Book.cs: Keep your Book class withId,Title,Author,IsAvailable, and theBorrow()andReturn()methods.User.cs: Keep your User class with borrowing tracking,MaxBooksconstant,CanBorrow(), and methods for managing borrowed books.Library.cs: Keep your Library class with borrowing logic, search methods, and theGetAllBooks()andGetAllUsers()methods.LibraryTests.cs: This is your new test file! Create a test class that verifies your library system works correctly. You'll write test methods that check various scenarios and report whether each test passes or fails.Program.cs: Your entry point that runs the tests and displays results.
Your test class should include methods that test these scenarios:
TestBookBorrow- Verify that borrowing an available book succeeds and changes its availabilityTestBookBorrowUnavailable- Verify that borrowing an already-borrowed book fails appropriatelyTestUserBorrowingLimit- Verify that users cannot borrow more than 3 booksTestBookReturn- Verify that returning a borrowed book works correctlyTestSearchByAuthor- Verify that searching by author returns the correct books
Each test method should return a boolean indicating whether the test passed. Create a simple test runner that executes all tests and prints the results.
You will receive the following inputs:
- Number of test scenarios to run (1-5, corresponding to the tests listed above)
For each test that runs, print the result in the format:
{TestName}: {PASS|FAIL}After all tests complete, print a summary:
--- Test Summary ---
Passed: {count}
Failed: {count}
Total: {count}For example, if the input is:
3The output should be:
TestBookBorrow: PASS
TestBookBorrowUnavailable: PASS
TestUserBorrowingLimit: PASS
--- Test Summary ---
Passed: 3
Failed: 0
Total: 3Another example with all 5 tests:
5The output should be:
TestBookBorrow: PASS
TestBookBorrowUnavailable: PASS
TestUserBorrowingLimit: PASS
TestBookReturn: PASS
TestSearchByAuthor: PASS
--- Test Summary ---
Passed: 5
Failed: 0
Total: 5Your tests should create fresh instances of Book, User, and Library for each test to ensure isolation. This way, one test's state doesn't affect another. Think of each test as a self-contained verification of a specific behavior in your system!
Try it yourself
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}");
}
}
}
}
}
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator3Class Architecture
Instance vs Static Data'readonly' & 'const' KeywordsBacking FieldsRecap - Bank Account Manager6Encapsulation
Access ModifiersProperties for EncapsulationData Hiding ImplementationImmutability PatternsRecap - Student Records12Project: Library System
Project StructureBook and User Models