Learning Objectives
After completing this skill, you will be able to:
- Build a complete WebApi integration testing architecture
- Use
IExceptionHandlerto implement modern exception handling - Validate the
ProblemDetailsandValidationProblemDetailsstandard formats - Use Flurl to simplify URL building for HTTP tests
- Use AwesomeAssertions for precise HTTP response validation
- Set up a multi-container (PostgreSQL + Redis) test environment
Core Concepts
IExceptionHandler - Modern Exception Handling
The IExceptionHandler interface introduced in ASP.NET Core 8+ provides a more elegant way to handle errors than traditional middleware.GlobalExceptionHandler Based on the exception type (KeyNotFoundException → 404, ArgumentException → 400, others → 500), it generates the corresponding ProblemDetails response.
ProblemDetails Standard Format
The unified error response format defined by RFC 7807:
| Field | Description |
|---|---|
type |
URI of the problem type |
title |
Brief error description |
status |
HTTP status code |
detail |
Detailed error explanation |
instance |
URI of the instance where the problem occurred |
ValidationProblemDetails - For Validation Errors
Inherits from ProblemDetails and additionally contains an errors dictionary that records the validation error message for each field.
FluentValidation Exception Handler
The FluentValidation exception handler implements the IExceptionHandler interface and specifically handles ValidationException, converting validation errors into the standard ValidationProblemDetails format response. Handlers execute in the order they are registered, and specific handlers (such as FluentValidation) must be registered before the global handler.
For the complete implementation code, see references/exception-handler-details.md
Integration Testing Infrastructure
The test infrastructure consists of three core components:
-
TestWebApplicationFactory: Inherits
WebApplicationFactory<Program>, configures multiple containers (PostgreSQL + Redis) and DI replacements (such as FakeTimeProvider)FakeTimeProvideris shared by all tests in the Collection and can only move forward; do not reset it to a fixed starting point at the beginning of each test, and instead calculate time assertions usingFactory.TimeProvider.GetUtcNow(). -
IntegrationTestCollection: Collection Fixture definition, ensuring container sharing
-
IntegrationTestBase: Test base class, providing HttpClient, DatabaseManager, FlurlClient, and time control methods
For the complete infrastructure code, see references/test-infrastructure.md
Flurl Simplifies URL Building
// 傳統方式
var url = $"/products?pageSize={pageSize}&page={page}&keyword={keyword}";
// 使用 Flurl
var url = "/products"
.SetQueryParam("pageSize", 5)
.SetQueryParam("page", 2)
.SetQueryParam("keyword", "特殊");
Test Examples
Covers complete test examples such as successfully creating a product (201 Created), validation errors (400 BadRequest + ValidationProblemDetails), resource not found (404 NotFound + ProblemDetails), and paged queries (200 OK + PagedResult), as well as the TestHelpers data management strategy.
For complete test examples and data management code, see references/test-examples.md
Best Practices
1. Test Structure Design
- Single Responsibility: Each test focuses on one specific scenario
- 3A Pattern: Clearly distinguish Arrange, Act, Assert
- Clear Naming: Method names express test intent
2. Error Handling Verification
- ValidationProblemDetails: Verify error response format
- ProblemDetails: Verify business exception responses
- HTTP Status Codes: Confirm correct status codes
3. Performance Considerations
- Container Sharing: Use Collection Fixture
- Data Cleanup: Clean up data after tests, do not rebuild containers
- Parallel Execution: Ensure test independence
Dependencies
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="AwesomeAssertions" Version="9.4.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="4.11.0" />
<PackageReference Include="Testcontainers.Redis" Version="4.11.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" />
<PackageReference Include="Flurl" Version="4.0.0" />
<PackageReference Include="Respawn" Version="7.0.0" />
Project Structure
src/
├── Api/ # WebApi 層
├── Application/ # 應用服務層
├── Domain/ # 領域模型
└── Infrastructure/ # 基礎設施層
tests/
└── Integration/
├── Fixtures/
│ ├── TestWebApplicationFactory.cs
│ ├── IntegrationTestCollection.cs
│ └── IntegrationTestBase.cs
├── Handlers/
│ ├── GlobalExceptionHandler.cs
│ └── FluentValidationExceptionHandler.cs
├── Helpers/
│ ├── DatabaseManager.cs
│ └── TestHelpers.cs
├── SqlScripts/
│ └── Tables/
└── Controllers/
└── ProductsControllerTests.cs
Output Format
- Generate
TestWebApplicationFactory.cs, configure multiple containers (PostgreSQL + Redis) and DI replacement - Generate
IntegrationTestCollection.csandIntegrationTestBase.cstest infrastructure - Generate
DatabaseManager.cs, integrate Respawn for test data cleanup - Generate controller test classes, verify CRUD, ProblemDetails and ValidationProblemDetails
- Generate
GlobalExceptionHandler.csandFluentValidationExceptionHandler.csexception handlers
Reference Resources
Example Files
- templates/test-web-application-factory.cs -
TestWebApplicationFactory, usingIAsyncLifetimeto manage container lifecycle - templates/integration-test-base.cs -
IntegrationTestBasebase class andIntegrationTestCollectionshared container configuration - templates/database-manager.cs -
DatabaseManager, responsible for initialization and reset of the test database - templates/create-products-table.sql - DDL for the products table, demonstrating externalization of SQL scripts
- templates/products-controller-tests.cs - Complete CRUD tests for the Controller, including request and response models
- templates/global-exception-handler.cs - Implement
IExceptionHandlerglobal exception handler - templates/fluent-validation-exception-handler.cs - FluentValidation exception handler, returns
ValidationProblemDetails. Registration order must precede the global handler - templates/test-project.csproj - Test project configuration (net9.0, xUnit 2.9.3, AwesomeAssertions 9.4.0)
Original Article
The content of this skill is distilled from the "Old-School Software Engineer's Testing Training - 30-Day Challenge" article series:
- Day 23 - Integration Testing in Practice: Integration Testing for WebApi Services
- Ironman Series Article:https://ithelp.ithome.com.tw/articles/10376873
- Sample Code:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day23
Official Documentation
- ASP.NET Core Integration Testing
- IExceptionHandler Documentation
- ProblemDetails RFC 7807
- Testcontainers for .NET
- AwesomeAssertions
- Flurl HTTP Client
- Respawn
Related Skills
dotnet-testing-advanced-aspnet-integration-testing- ASP.NET Core Basic Integration Testingdotnet-testing-advanced-testcontainers-database- Database Container Testingdotnet-testing-advanced-testcontainers-nosql- NoSQL Container Testingdotnet-testing-fluentvalidation-testing- FluentValidation Testing