Unittestdemo
ASP.NET Core 8 API demo using Clean Architecture with PostgreSQL. Features student CRUD, grade calculation, mocked Email/SMS, unit & integration tests (xUnit, Moq, Coverlet, Testcontainers), and GitHub Actions CI with coverage reports.
Install / Use
/learn @T-T-Software-Solution/UnittestdemoREADME
Demo Unit Test Project

A comprehensive C# ASP.NET Core API project demonstrating unit testing best practices with Clean Architecture.
Features
- Clean Architecture with separation of concerns
- Grade calculation using weighted averages
- Student management with CRUD operations
- Notification system with Email/SMS mocking
- Comprehensive testing with 80% coverage requirement
- PostgreSQL database with Entity Framework Core
- API authentication (optional API key)
- Docker support with Testcontainers for integration tests
Project Structure
├── AppCore/Demo.AppCore/ # Application Core (Models, Interfaces, Services)
├── Infra/Demo.Database/ # Database Infrastructure (EF Core, Repositories)
├── Infra/Demo.Notification/ # Notification Services (Email, SMS mocks)
├── Presentations/Demo.Api/ # Web API Controllers
├── Tests/Demo.AppCore.Tests/ # Unit Tests (xUnit + Moq)
└── Tests/Demo.Api.IntegrationTests/ # Integration Tests (Testcontainers)
Technologies Used
- .NET 8.0
- ASP.NET Core Web API
- Entity Framework Core
- PostgreSQL
- xUnit for testing
- Moq for mocking
- Testcontainers for integration testing
- Coverlet for code coverage
- GitHub Actions for CI/CD
Getting Started
Prerequisites
- .NET 8.0 SDK
- PostgreSQL (or Docker for development)
- Git
Database Setup
The application uses PostgreSQL with the following default connection for development:
Server=localhost;Port=65432;Database=demounittest01;User ID=admin;Password=admin;Include Error Detail=true;
Using Real Database with User Secrets (Recommended)
For production or when using a real database, use .NET User Secrets to store sensitive connection strings:
Step 1: Initialize User Secrets
cd Presentations/Demo.Api
dotnet user-secrets init
Step 2: Set Connection String
# Replace with your actual database connection string
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=your-server;Port=5432;Database=your-db;User ID=your-user;Password=your-password;Include Error Detail=true;"
Step 3: Verify Secrets
dotnet user-secrets list
Environment Variables (Alternative) You can also use environment variables:
# Windows
set ConnectionStrings__DefaultConnection="Server=your-server;Port=5432;Database=your-db;User ID=your-user;Password=your-password"
# Linux/macOS
export ConnectionStrings__DefaultConnection="Server=your-server;Port=5432;Database=your-db;User ID=your-user;Password=your-password"
Production Configuration For production deployments, use:
- Azure Key Vault
- AWS Secrets Manager
- Kubernetes Secrets
- Environment variables in secure hosting platforms
⚠️ Security Note: Never commit connection strings with credentials to source control. Always use user secrets, environment variables, or secure key management services.
Running the Application
- Clone the repository
- Restore dependencies:
dotnet restore Demo.sln - Build the solution:
dotnet build Demo.sln - Run the API:
dotnet run --project Presentations/Demo.Api/Demo.Api.csproj
Running Tests
Unit Tests:
dotnet test Tests/Demo.AppCore.Tests/Demo.AppCore.Tests.csproj
Integration Tests:
dotnet test Tests/Demo.Api.IntegrationTests/Demo.Api.IntegrationTests.csproj
All Tests:
dotnet test Demo.sln
Code Coverage Reports
This project uses Coverlet for code coverage collection and ReportGenerator for creating HTML reports.
Prerequisites
Install ReportGenerator as a global tool:
dotnet tool install -g dotnet-reportgenerator-globaltool
Generate Coverage Reports
Step 1: Run Tests with Coverage Collection
dotnet test Demo.sln --collect:"XPlat Code Coverage" --results-directory ./TestResults
Step 2: Generate HTML Coverage Report
reportgenerator -reports:"TestResults\**\coverage.cobertura.xml" -targetdir:"TestResults\CoverageReport" -reporttypes:"Html"
ถ้ารันไม่ได้ใน Mac แนะนำให้ลองรัน Command นี้
~/.dotnet/tools/reportgenerator -reports:"TestResults/**/*.cobertura.xml" -targetdir:"TestResults/CoverageReport" -reporttypes:"Html"
Step 3: View the Report
Open TestResults\CoverageReport\index.html in your web browser.
Alternative Report Formats
Generate multiple report formats:
# HTML + XML + Text Summary
reportgenerator -reports:"TestResults\**\coverage.cobertura.xml" -targetdir:"TestResults\CoverageReport" -reporttypes:"Html;Cobertura;TextSummary"
# Just text summary for quick viewing
reportgenerator -reports:"TestResults\**\coverage.cobertura.xml" -targetdir:"TestResults\CoverageReport" -reporttypes:"TextSummary"
Coverage Report Features
The generated HTML report includes:
- 📊 Overall Coverage Metrics: Line coverage, branch coverage, assembly summary
- 🔍 Detailed Class Views: Line-by-line coverage for each class
- 📱 Interactive Interface: Sortable tables, search functionality, responsive design
- 🎨 Visual Indicators: Color-coded coverage (green = covered, red = not covered)
- 📈 Historical Tracking: Coverage trends when run multiple times
Coverage Requirements
- Minimum Coverage: 80% line coverage required
- Current Coverage: 82% ✅ (exceeds requirement)
- Enforcement: GitHub Actions will fail builds below 80% coverage
One-Command Coverage Report
For convenience, you can combine both steps:
# Windows
dotnet test Demo.sln --collect:"XPlat Code Coverage" --results-directory ./TestResults && reportgenerator -reports:"TestResults\**\coverage.cobertura.xml" -targetdir:"TestResults\CoverageReport" -reporttypes:"Html" && start TestResults\CoverageReport\index.html
# Alternative (without auto-open)
dotnet test Demo.sln --collect:"XPlat Code Coverage" --results-directory ./TestResults && reportgenerator -reports:"TestResults\**\coverage.cobertura.xml" -targetdir:"TestResults\CoverageReport" -reporttypes:"Html"
API Endpoints
GET /healthz- Health checkGET /api/students- Get all studentsGET /api/students/{id}- Get student by IDGET /api/students/{id}/grade- Get student's calculated gradePOST /api/students- Create new studentPUT /api/students/{id}- Update studentDELETE /api/students/{id}- Delete student
Grade Calculation
Grades are calculated using weighted averages:
- Formula:
total = Σ(score/maxScore * weight) - Final Grade:
(total / weightSum) * 100 - Letter Grades: A (≥90%), B (80-89%), C (70-79%), D (60-69%), F (<60%)
Testing Strategy
The project follows the AAA pattern (Arrange, Act, Assert):
- Unit Tests: Test individual components in isolation using mocks
- Integration Tests: Test complete workflows with real database via Testcontainers
- Coverage Gate: Minimum 80% code coverage required for builds
CI/CD Pipeline
GitHub Actions workflow includes:
- Automated testing on push/PR
- Code coverage reporting
- Coverage threshold enforcement (80%)
- Automated builds and deployments
Authentication
Optional API key authentication can be enabled in production:
- Set
ApiKeyRequired: truein configuration - Include
X-Api-Keyheader with requests - Default key:
demo-api-key-12345
Screenshots and Examples
VS Code Test Runner

Code Coverage Report

GitHub Actions CI/CD Pipeline

CI/CD Artifacts

Additional Resources
This project demonstrates practical unit testing techniques and Clean Architecture patterns. For detailed insights on creating effective unit tests, check out these articles:
📚 Related Articles (Thai):
- การสร้าง Unit Test ที่มีประสิทธิภาพ ลดภาระ เพิ่มคุณค่า เพื่อ Unit Test ที่ยั่งยืน - Best practices for sustainable unit testing
- การสร้าง Unit Test ที่มีประสิทธิภาพ 2 ต่อจาก Week ที่แล้ว - Advanced unit testing patterns and techniques
These articles provide deep insights into the principles and practices demonstrated in this codebase.
Contributing
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Ensure all tests pass and coverage meets requirements
- Submit a pull request
Related Skills
feishu-drive
349.2k|
things-mac
349.2kManage Things 3 via the `things` CLI on macOS (add/update projects+todos via URL scheme; read/search/list from the local Things database)
clawhub
349.2kUse the ClawHub CLI to search, install, update, and publish agent skills from clawhub.com
codebase-memory-mcp
1.2kHigh-performance code intelligence MCP server. Indexes codebases into a persistent knowledge graph — average repo in milliseconds. 66 languages, sub-ms queries, 99% fewer tokens. Single static binary, zero dependencies.
