A production-grade .NET solution demonstrating comprehensive test coverage and QA best practices β unit testing, integration testing, architecture enforcement, and load testing β across a layered Library Management API.
LibrarySystem/
β
βββ π src/
β βββ LibrarySystem.API/ # ASP.NET Core Web API (Minimal API + Controllers)
β β βββ Controllers/ # LoansController, MembersController
β β βββ Program.cs # App entry point, DI, Minimal API endpoints
β β βββ LibrarySystem.API.csproj
β β
β βββ LibrarySystem.Data/ # Data Access Layer (EF Core)
β β βββ Entities/ # Book, Member, Loan
β β βββ LibraryDbContext.cs # EF Core DbContext with Fluent API config
β β βββ LibrarySystem.Data.csproj
β β
β βββ LibrarySystem.Services/ # Business Logic Layer
β βββ Interfaces/ # IBookService, ILoanService, IMemberService
β βββ BookService.cs
β βββ LoanService.cs
β βββ MemberService.cs
β βββ LibrarySystem.Services.csproj
β
βββ π tests/
β βββ LibrarySystem.UnitTests/ # β
Unit Tests
β βββ LibrarySystem.IntegrationTests/ # β
Integration Tests
β βββ LibrarySystem.ArchitectureTests/# β
Architecture Tests
β βββ LibrarySystem.LoadTests/ # β
Load / Performance Tests
β
βββ LibrarySystem.slnx # Solution file
Isolated tests validating individual service methods against an EF Core InMemory database, ensuring business logic correctness without any external dependencies.
| Aspect | Detail |
|---|---|
| Project | LibrarySystem.UnitTests |
| Framework | xUnit |
| Assertions | Shouldly β fluent, readable assertion library |
| Mocking | Moq β available for interface mocking |
| Database | EF Core InMemory Provider β unique Guid database per test for full isolation |
| Coverage | Coverlet β code coverage collection |
| Test Count | 17 tests across 3 test classes |
Test Classes:
BookServiceUnitTestsβ GetAll, GetById, GetById_NotFoundLoanServiceUnitTestsβ Borrow/Return flows, expired membership, max loans, no stock, outstanding fines, overdue fines, double returnMemberServiceUnitTestsβ Registration, duplicate email detection
End-to-end HTTP pipeline tests using WebApplicationFactory<T> to spin up an in-memory test server, exercising the full request pipeline β routing, middleware, DI, and database β without a running server.
| Aspect | Detail |
|---|---|
| Project | LibrarySystem.IntegrationTests |
| Framework | xUnit + IClassFixture<WebApplicationFactory<Program>> |
| Assertions | Shouldly |
| Test Server | Microsoft.AspNetCore.Mvc.Testing (WebApplicationFactory) |
| Database | EF Core InMemory β seeded per test via DI scope |
| Coverage | Coverlet |
| Test Count | 10 tests across 3 test classes |
Test Classes:
BookEndpointsIntegrationTestsβGET /api/books,GET /api/books/{id}(OK + NotFound)LoanEndpointsIntegrationTestsβPOST /api/loans(Borrow),PUT /api/loans/{id}/return(Return), NotFound, BadRequest (out-of-stock, already returned)MemberEndpointsIntegrationTestsβPOST /api/members(Register), BadRequest (duplicate email)
Automated enforcement of structural rules and dependency boundaries using reflection-based analysis, preventing architectural erosion over time.
| Aspect | Detail |
|---|---|
| Project | LibrarySystem.ArchitectureTests |
| Framework | xUnit |
| Assertions | Shouldly |
| Rule Engine | NetArchTest.Rules β fluent API for .NET architecture constraints |
| Coverage | Coverlet |
| Test Count | 6 tests |
Enforced Rules:
| # | Rule | Purpose |
|---|---|---|
| 1 | Data layer β β Services, API |
Domain layer must not depend on higher layers |
| 2 | Services layer β β API |
Services must not depend on the presentation layer |
| 3 | Interfaces start with "I" |
Naming convention enforcement |
| 4 | Service classes end with "Service" |
Naming convention enforcement |
| 5 | Controllers end with "Controller" |
Naming convention enforcement |
| 6 | Controllers inherit ControllerBase |
Structural inheritance verification |
HTTP-based load testing against a running instance of the API to validate throughput, latency, and stability under sustained concurrent traffic.
| Aspect | Detail |
|---|---|
| Project | LibrarySystem.LoadTests |
| Framework | NBomber + NBomber.Http |
| Execution | Standalone console application (dotnet run) |
| Scenario | book_catalog_load_test β sustained load on GET /api/books |
| Load Profile | 50 concurrent connections for 30 seconds with a 5-second warmup |
β οΈ Note: The API must be running locally onhttp://localhost:5280before executing load tests.
All automated tests (Unit, Integration, Architecture) achieve complete isolation without Docker or external services:
| Mechanism | Used By | Description |
|---|---|---|
| EF Core InMemory Database | Unit Tests | Each test creates a fresh Guid-named in-memory database, ensuring zero cross-test contamination. |
| WebApplicationFactory<Program> | Integration Tests | Creates an in-memory HTTP test server with a replaced DbContext. Each test explicitly calls EnsureDeleted() + EnsureCreated() for a clean slate. |
| NetArchTest Reflection | Architecture Tests | Pure reflection-based assembly scanning β no state or infrastructure required. |
| Standalone Process | Load Tests | Executes against a live API instance. No test containers β the API is expected to be running externally. |
No Docker Compose, Testcontainers, or external broker configurations are required to run the automated test suite.
- .NET 10.0 SDK or later
- A terminal / PowerShell / Git Bash
Execute all Unit, Integration, and Architecture tests across the entire solution:
dotnet testTarget a single test project by name:
# Unit Tests only
dotnet test LibrarySystem.UnitTests
# Integration Tests only
dotnet test LibrarySystem.IntegrationTests
# Architecture Tests only
dotnet test LibrarySystem.ArchitectureTestsUse --filter to execute specific tests:
# Run a single test by display name
dotnet test --filter "BorrowBookAsync_ShouldSucceedAndDecrementStock_WhenAllRulesPass"
# Run all tests in a specific class
dotnet test --filter "FullyQualifiedName~LoanServiceUnitTests"
# Run all Architecture tests
dotnet test --filter "FullyQualifiedName~ArchitectureTests"Generate a coverage report using Coverlet and visualize it with ReportGenerator:
# Step 1: Install ReportGenerator global tool (one-time)
dotnet tool install -g dotnet-reportgenerator-globaltool
# Step 2: Run tests with coverage collection (Cobertura format)
dotnet test --collect:"XPlat Code Coverage" --results-directory ./TestResults
# Step 3: Generate the HTML report
reportgenerator -reports:./TestResults/**/coverage.cobertura.xml -targetdir:./CoverageReport
# Step 4: Open the report
# Windows:
start ./CoverageReport/index.html
# macOS:
open ./CoverageReport/index.html
# Linux:
xdg-open ./CoverageReport/index.htmlStep 1: Start the API first:
dotnet run --project LibrarySystem.APIStep 2: In a separate terminal, run the load test:
dotnet run --project LibrarySystem.LoadTestsReports are generated in LibrarySystem.LoadTests/bin/Debug/net10.0/reports/.
| Category | Library | Version | Purpose |
|---|---|---|---|
| Test Framework | xUnit | 2.9.3 | Test runner and discovery |
| Assertions | Shouldly | 4.3.0 | Fluent, expressive assertions |
| Mocking | Moq | 4.20.72 | Interface/class mocking |
| In-Memory DB | EF Core InMemory | 10.0.8 | Database isolation for tests |
| Architecture Rules | NetArchTest.Rules | 1.3.2 | Assembly-level constraint testing |
| Load Testing | NBomber | 6.4.1 | HTTP load & performance testing |
| Code Coverage | Coverlet | 10.0.1 | Coverage data collection |
| Test Server | Mvc.Testing | 10.0.8 | WebApplicationFactory for integration tests |
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β LibrarySystem.API β
β (Minimal API + Controllers) β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β LibrarySystem.Services β
β (BookService / LoanService / MemberService) β
βββββββββββββββββββββββββββββββββββββββββββββββββββ€
β LibrarySystem.Data β
β (LibraryDbContext / EF Core Entities) β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
Dependency Direction: API β Services β Data
Architecture tests enforce this dependency direction at build time, preventing any layer from referencing a layer above it.
This project is licensed under the MIT License. Feel free to fork, adapt, and use it as a reference for testing strategies in your own .NET projects.
Built with β€οΈ by Ahmed Anwer.