Migration ist nicht alles: Monitoring und Telemetrie
Moderne Entwicklungs-Stacks wie .NET Core bieten mittlerweile zahlreiche Funktionen zur Unterstützung von Monitoring, Telemetrie und flexibler Konfiguration, Dies sollte bei der Migration einer Legacy-Anwendung unbedingt mit berücksichtigt werden.
Einleitung
-
nahtloses Deployment (Stichwort: DevOps bzw. DevSecOps),
-
(horizontale) Skalierung,
-
Telemetrie/Überwachung,
-
die Neuerstellung eines Controller-basierten ASP.NET-Systems mit Frontend und Backend für eine Verwertungsgesellschaft, mit Kubernetes und ASP.NET Core als Zielumgebung,
-
die Modernisierung einer Remote-Control-Website inklusive der zugehörigen Dienste und VPN-Infrastruktur zur Steuerung entfernter Geräte per API und VNC, ebenfalls migriert auf die .NET (Core)-Plattform,
-
die Migration einer Xamarin-Forms-App zur Gerätesteuerung (über UDP Broadcast und HTTP REST API) zu .NET MAUI mit Unterstützung der Plattformen iOS, Android und Windows UWP.
Ablösung von dateibasiertem Logging durch OpenTelemetry
-
Zentralisierte Überwachung: OpenTelemetry ermöglicht es, Logdaten aus verschiedenen Diensten und Komponenten an einem zentralen Ort zu sammeln und zu analysieren. Dies erleichtert die Überwachung und Fehlersuche in komplexen, verteilten Systemen.
-
Standardisierung: Als Open-Source-Projekt bietet OpenTelemetry einen einheitlichen Standard für Telemetriedaten. Dies fördert die Interoperabilität zwischen verschiedenen Tools und Plattformen und verhindert Vendor-Lock-in.
-
Skalierbarkeit: OpenTelemetry ist für den Einsatz in groß angelegten, skalierbaren Anwendungen konzipiert. Es kann (abhängig vom Backend) große Mengen an Telemetriedaten effizient verarbeiten, ohne die Anwendungsleistung zu beeinträchtigen.
-
Verbesserte Einblicke: Durch die Unterstützung von verteiltem Tracing können Entwickler die Ausführung von Anfragen über verschiedene Dienste hinweg nachverfolgen. Dies bietet tiefere Einblicke in das Systemverhalten und hilft bei der schnellen Identifizierung von Engpässen.
-
Cloud-Native Unterstützung: OpenTelemetry ist speziell für cloudbasierte Umgebungen entwickelt worden und integriert sich nahtlos mit modernen Cloud-Technologien und -Diensten.
-
Reduzierter Wartungsaufwand: Da OpenTelemetry als Managed Service oder in Form von leicht zu aktualisierenden Bibliotheken bereitgestellt wird, reduziert sich der Aufwand für Wartung und Aktualisierung im Vergleich zu individuellen Logging-Lösungen.
-
In der Entwicklungsumgebung reicht meist das Aspire Dashboard
-
Open Source: Open Telemetry Collector, Prometheus, Loki, Grafana oder der ELK Stack (ElasticSearch / Kibana)
Ersatz der "klassischen" Konfiguration per ConfigurationManager durch die IConfiguration Schnittstelle
-
Flexibilität bei Konfigurationsquellen: `IConfiguration` unterstützt eine Vielzahl von Konfigurationsquellen wie JSON-, XML- und INI-Dateien, Umgebungsvariablen, Befehlszeilenargumente und benutzerdefinierte Quellen. Dies ermöglicht es, Konfigurationen dynamisch und flexibel zu gestalten, ohne den Code ändern zu müssen.
-
Einfache Integration von Umgebungsvariablen: In Cloud-Umgebungen ist es üblich (und sinnvoll), Konfigurationen über Umgebungsvariablen zu steuern. `IConfiguration` integriert diese nahtlos, was die Anpassung der Anwendung an verschiedene Umgebungen erleichtert.
-
Unterstützung von Abhängigkeitsinjektion: `IConfiguration` ist vollständig in das Dependency Injection-System von ASP.NET Core integriert. Dies fördert lose Kopplung und erleichtert das Testen und die Wartung der Anwendung.
-
Hierarchische Konfigurationen: Mit `IConfiguration` können Konfigurationswerte hierarchisch strukturiert werden. Dies ermöglicht eine übersichtlichere Organisation komplexer Konfigurationsdaten.
-
Laufzeitaktualisierung: Bestimmte Konfigurationsquellen können so eingerichtet werden, dass sie zur Laufzeit aktualisiert werden. Dies ist besonders nützlich in Umgebungen, in denen Downtime minimiert werden muss.
-
Cloud-Native Ausrichtung: `IConfiguration` ist für moderne, Cloud-native Anwendungen konzipiert und unterstützt die Prinzipien der 12-Factor App Methodik, was die Skalierbarkeit und Portabilität der Anwendung verbessert.
-
Umgebungsabhängige Konfiguration: Umgebungsvariablen ermöglichen es, Konfigurationen einfach zwischen Entwicklungs-, Test- und Produktionsumgebungen zu variieren, ohne den Anwendungscode zu ändern.
-
Sicherheit: Sensible Daten wie Verbindungszeichenfolgen oder API-Schlüssel können sicher über Umgebungsvariablen verwaltet werden, anstatt sie in Konfigurationsdateien oder im Code zu hinterlegen.
-
Einfache Skalierung und Bereitstellung: In Cloud-Umgebungen und Container-Orchestrierungssystemen wie Kubernetes oder Docker Swarm können Umgebungsvariablen zentral verwaltet und automatisch an neue Instanzen verteilt werden.
-
Reduzierung von Konfigurationsfehlern: Da Umgebungsvariablen außerhalb der Anwendung verwaltet werden, verringert sich das Risiko von Konfigurationsfehlern beim Deployment.
-
Kontinuierliche Integration und Deployment (CI/CD): Umgebungsvariablen lassen sich leicht in CI/CD-Pipelines integrieren, was automatisierte Deployments in verschiedene Umgebungen erleichtert.
ASP.NET HealthChecks: Verwendung und Vorteile
-
Frühe Erkennung von Problemen: HealthChecks ermöglichen eine frühzeitige Erkennung von Problemen, bevor diese das Endnutzer-Erlebnis beeinträchtigen. Das Monitoring kann regelmäßig überprüfen, ob alle Komponenten der Anwendung einwandfrei funktionieren.
-
Einfache Integration: ASP.NET HealthChecks sind leicht - auch nachträglich - in bestehende Anwendungen zu integrieren, da sie Teil des ASP.NET Core Frameworks sind und über einfache Konfigurationen gesteuert werden können.
-
Automatisiertes Monitoring und Alerting: Die Ergebnisse von HealthChecks können in Monitoring-Systeme wie Prometheus, Grafana oder Azure Application Insights integriert werden. Dies ermöglicht es, bei Problemen sofortige Benachrichtigungen zu senden.
-
Anpassbarkeit und Erweiterbarkeit: ASP.NET Core bietet eine Vielzahl an vordefinierten HealthChecks, und Entwickler können eigene Checks implementieren, die spezifisch auf die Bedürfnisse ihrer Anwendung zugeschnitten sind.
-
SQL Server HealthCheck: Prüft die Erreichbarkeit und Funktionalität eines SQL Server Datenbankservers, indem eine einfache Abfrage ausgeführt wird. Dies ist nützlich, um sicherzustellen, dass die Verbindung zur Datenbank ordnungsgemäß funktioniert.
-
Redis HealthCheck: Überprüft die Erreichbarkeit eines Redis-Caches. Dies ist wichtig, wenn Ihre Anwendung Redis als Speicher oder Cache verwendet, um sicherzustellen, dass Daten schnell und zuverlässig abgerufen werden können.
-
Uri HealthCheck: Prüft, ob eine externe API oder ein Webdienst erreichbar ist. Dieser HealthCheck kann verwendet werden, um sicherzustellen, dass alle externen Abhängigkeiten Ihrer Anwendung verfügbar sind.
-
Disk Storage HealthCheck: Überwacht den Speicherplatz auf einem bestimmten Laufwerk, um sicherzustellen, dass Ihre Anwendung genügend Speicherplatz hat, um ordnungsgemäß zu funktionieren.
-
Memory HealthCheck: Überprüft, ob Ihre Anwendung innerhalb der konfigurierten Grenzen für den Speicherverbrauch bleibt, um Out-Of-Memory-Fehler zu vermeiden.
services.AddHealthChecks()
.AddSqlServer(Configuration.GetConnectionString("DefaultConnection"))
.AddRedis(Configuration["RedisConnectionString"])
.AddCheck<CustomHealthCheck>("custom_check");
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
});
dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
services.AddHealthChecksUI()
.AddInMemoryStorage();
:
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecksUI();
});
Einsatz eines Übersichts-Dashboards
Überarbeitung von APIs, ggf. Ersatz controllerbasierter APIs durch MinimalAPIs oder FastEndpoints
-
Weniger Boilerplate-Code: Minimal APIs reduzieren den notwendigen Code erheblich. Entwickler können Endpunkte direkt in der `Program.cs` definieren, ohne separate Controller-Klassen und umfangreiche Routing-Konfigurationen erstellen zu müssen.
-
Verbesserte Performance: Durch den geringeren Overhead und die vereinfachte Middleware-Pipeline bieten Minimal APIs oft eine bessere Performance im Vergleich zu traditionellen MVC-Controllern. Minimal APIs werden auch von der nativen AOT Kompilierung unterstützt (im Gegensatz zum MVC Ansatz) - die zu einem wesentlichen Performancegewinn führen kann.
-
Einfachheit und Schnellere Entwicklung: Sie ermöglichen einen schnelleren Start bei der Entwicklung kleinerer Dienste oder Microservices, da weniger Setup und Konfiguration erforderlich sind.
-
Direkte Kontrolle über Routing und Middleware: Entwickler haben mehr Flexibilität bei der Definition von Routen und können Middleware gezielter einsetzen.
-
Schnelleres Anwendungsstartverhalten: Weniger initialisierte Komponenten führen zu kürzeren Startzeiten der Anwendung.
-
Strukturierte Organisation: FastEndpoints bietet eine klare Trennung zwischen Anfrage, Verarbeitung und Antwort durch die Verwendung von dedizierten Endpoint-Klassen. Dies fördert sauberen und wartbaren Code.
-
Erweiterte Funktionalität ohne Overhead: Die Bibliothek fügt zusätzliche Features hinzu, wie z. B. automatisierte Validierung, ohne den Overhead von MVC-Controllern zu verursachen.
-
Verbesserte Performance: FastEndpoints ist für hohe Leistung optimiert und kann schneller sein als sowohl MVC-Controller als auch Minimal APIs, da es auf unnötige Abstraktionen verzichtet.
-
Integrierte Validierung und Filter: Eingebaute Unterstützung für Anfragevalidierung, Authentifizierung und Autorisierung erleichtert die Entwicklung sicherer APIs.
-
Konsistente Entwicklungserfahrung: Durch die Verwendung von Endpoint-Klassen fördert FastEndpoints konsistente Coding-Standards innerhalb eines Teams oder Projekts.
-
Flexibilität und Anpassbarkeit: Entwickler können die Pipeline leicht erweitern und anpassen, was bei MVC-Controllern oft komplizierter ist.
-
Bessere Unterstützung für API-Versionierung und Dokumentation: FastEndpoints erleichtert die Implementierung von API-Versionierung und die Integration von Tools wie Swagger für die API-Dokumentation.
Verwendung von Problemdetails nach RFC-9457
-
Standardisierte Fehlerantworten: Durch die Implementierung von ProblemDetails erhalten Clients Fehlerinformationen in einem einheitlichen Format, was die Verarbeitung und Anzeige von Fehlern vereinfacht.
-
Konsistente und detaillierte Fehlerinformationen: ProblemDetails ermöglicht es, zusätzliche Details wie `type`, `title`, `status`, `detail` und `instance` in Fehlerantworten einzuschließen. Dies erleichtert das Debugging und die Fehlerbehebung.
-
Verbesserte Sicherheit: Mit `app.UseExceptionHandler()` können ggf. Ausnahmen zentral abgefangen und behandelt werden, ohne dabei sensible Informationen an den Client preiszugeben.
-
Anpassbare Fehlerbehandlung: Durch die Registrierung von `builder.Services.AddProblemDetails()` können Entwickler die Darstellung von Fehlern anpassen, z. B. durch Hinzufügen benutzerdefinierter Fehlercodes oder zusätzlicher Metadaten.
-
Unterstützung für verschiedene HTTP-Statuscodes: `app.UseStatusCodePages()` stellt sicher, dass auch HTTP-Statuscodes ohne Inhalt (wie 404 oder 500) eine informative Antwort im ProblemDetails-Format liefern.
-
builder.Services.AddProblemDetails():
-
Registrierung der Dienste: Diese Methode fügt die erforderlichen Dienste zur Verwendung von ProblemDetails hinzu.
-
Konfigurationsmöglichkeiten: Entwickler können Optionen festlegen, wie z. B. das Einbinden von Ausnahmeinformationen oder das Anpassen von Fehlerantworten je nach Umgebung (Entwicklung, Staging, Produktion).
-
Beispiel:
builder.Services.AddProblemDetails(options =>
{
options.IncludeExceptionDetails = (context, exception) =>
{
// Ausnahmeinformationen nur in der Entwicklungsumgebung einbinden
var env = context.RequestServices.GetRequiredService<IHostEnvironment>();
return env.IsDevelopment();
};
});
-
app.UseExceptionHandler():
-
Globale Ausnahmebehandlung: Diese Middleware fängt alle unbehandelten Ausnahmen ab, die in der Anwendung auftreten.
-
Integration mit ProblemDetails: In Kombination mit ProblemDetails können detaillierte Fehlerinformationen bereitgestellt werden, ohne interne Implementierungsdetails preiszugeben.
-
Sicherheitsaspekt: Verhindert die Weitergabe von Stack-Traces oder sensiblen Informationen an den Client.
-
app.UseStatusCodePages():
-
Behandlung von Statuscodes ohne Inhalt: Diese Middleware generiert Antworten für HTTP-Statuscodes, die normalerweise keinen Inhalt haben.
-
Konsistente Fehlerdarstellung: Stellt sicher, dass auch für Fehler wie 404 (Nicht gefunden) oder 401 (Nicht autorisiert) informative Antworten im ProblemDetails-Format gesendet werden.
-
Beispielkonfiguration:
app.UseStatusCodePages(context =>
{
var problemDetailsFactory = context.HttpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>();
var problemDetails = problemDetailsFactory.CreateProblemDetails(context.HttpContext, context.HttpContext.Response.StatusCode);
return context.HttpContext.Response.WriteAsJsonAsync(problemDetails);
});
-
Verbesserte Entwicklerproduktivität: Durch die zentrale Fehlerbehandlung und das standardisierte Format müssen Entwickler weniger Zeit auf die Implementierung individueller Fehlerlösungen verwenden.
-
Bessere Nutzererfahrung: Clients erhalten klare und konsistente Fehlermeldungen, was die Interaktion mit der API erleichtert.
-
Einfacheres Monitoring und Logging: Einheitliche Fehlerantworten erleichtern das Monitoring und die Analyse von Problemen in Produktionsumgebungen.
-
Flexibilität und Erweiterbarkeit: Die Konfiguration kann an die spezifischen Bedürfnisse der Anwendung angepasst werden, z. B. durch Hinzufügen von benutzerdefinierten Eigenschaften zu ProblemDetails.
builder.Services.AddProblemDetails(options =>
{
options.IncludeExceptionDetails = (context, exception) =>
{
// Ausnahmeinformationen nur in der Entwicklungsumgebung einschließen
var env = context.RequestServices.GetRequiredService<IHostEnvironment>();
return env.IsDevelopment();
};
});
// Weitere Service-Registrierungen
builder.Services.AddControllers();
var app = builder.Build();
// Verwendung von ExceptionHandler und StatusCodePages
app.UseExceptionHandler();
app.UseStatusCodePages();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Ersatz "schwergewichtiger" DB Server
-
Weniger RAM Bedarf (MS-SQL 1..4GB, MariaDB 512MByte, PostgreSQL 1GB)
-
Weniger Speicherplatz für DB Server (MS-SQL ca. 3GB, MariaDB 200.660MByte, PostgreSQL ca. 300MByte)
-
Niedrigere CPU Auslastung
-
Mehr Durchsatz bei vergleichbarer Konfiguration
Nutzung neuer Testmöglichkeiten, insbesondere für Integration-Tests
Microsoft.AspNetCore.Mvc.Testing
-Bibliothek. Diese bietet die Klasse WebApplicationFactory<TEntryPoint>
, mit der ein Testserver erstellt werden kann, der HTTP-Anfragen an die API entgegennimmt.Schritt 1: Einrichten des Testprojekts
-
Erstellung neues Testprojekt:Neues xUnit-Testprojekt zur Solution hinzufügen. Dies kann über Visual Studio oder die .NET CLI erfolgen:
dotnet new xunit -o YourApi.Tests
-
Projektreferenzen hinzufügen:Projektverweis auf API-Projekt hinzufügen, damit das Testprojekt auf die
Program
- oderStartup
-Klasse zugreifen kann. -
Erforderliche NuGet-Pakete installieren:Notwendige Pakete im Testprojekt installieren:
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package xunit
dotnet add package xunit.runner.visualstudio
dotnet add package Microsoft.NET.Test.Sdk
Schritt 2: Integrationstests mit WebApplicationFactory
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using Microsoft.AspNetCore.Mvc.Testing;
using YourApiNamespace; // Ersetzen Sie dies durch den Namespace Ihrer API
namespace YourApi.Tests
{
public class IntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public IntegrationTests(WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Get_Endpoint_Returns_Success()
{
// Arrange
var url = "/api/values"; // Passen Sie den Endpunkt entsprechend an
// Act
var response = await _client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode(); // Überprüft, ob der Statuscode 2xx ist
var responseString = await response.Content.ReadAsStringAsync();
Assert.NotNull(responseString);
// Weitere Überprüfungen können hier hinzugefügt werden
}
[Fact]
public async Task Post_Endpoint_Returns_Created()
{
// Arrange
var url = "/api/values";
var jsonContent = "{\"name\":\"Test Value\"}";
var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync(url, content);
// Assert
Assert.Equal(HttpStatusCode.Created, response.StatusCode);
var responseString = await response.Content.ReadAsStringAsync();
Assert.NotNull(responseString);
// Weitere Überprüfungen können hier hinzugefügt werden
}
}
}
Erläuterungen:
-
IClassFixture<WebApplicationFactory<Program>>:
- Dieses Interface stellt eine gemeinsame Instanz von
WebApplicationFactory
für alle Tests bereit. - Für .NET 6 und früher ist
Program
durchStartup
zu ersetzen.
- Dieses Interface stellt eine gemeinsame Instanz von
-
CreateClient():
- Erstellt einen
HttpClient
, der Anfragen an den in-memory Testserver sendet.
- Erstellt einen
-
Testmethoden ([Fact]):
- Verwendet
[Fact]
, um Testmethoden zu kennzeichnen. - Jede Methode sollte die Schritte Arrange, Act und Assert enthalten.
- Verwendet
Schritt 3: Anpassen WebApplicationFactory (Optional)
Wenn beispielsweise eine In-Memory-Datenbank verwendet werdem soll, kann die WebApplicationFactory
wie folgt erweitert werden:
Beispielcode:
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using YourApiNamespace;
namespace YourApi.Tests
{
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Entfernen der vorhandenen DbContext-Registrierung
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<YourDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Hinzufügen eines In-Memory-Datenbankkontexts
services.AddDbContext<YourDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Erstellen des Service Providers
var serviceProvider = services.BuildServiceProvider();
// Initialisieren der Datenbank
using (var scope = serviceProvider.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<YourDbContext>();
db.Database.EnsureCreated();
// Optionale Datenbank-Seeding-Methode
// SeedDatabase(db);
}
});
}
}
}
Erläuterungen:
-
CustomWebApplicationFactory:
- Ermöglicht es, die Konfiguration des Testservers anzupassen.
- Entfernt die vorhandene Datenbankkonfiguration und ersetzt sie durch eine In-Memory-Datenbank.
-
YourDbContext:
- Bitte durch eigenen DbContext ersetzen.
Schritt 4: Verwenden CustomWebApplicationFactory in Tests
Beispielcode:
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using YourApiNamespace;
namespace YourApi.Tests
{
public class IntegrationTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public IntegrationTests(CustomWebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Get_Endpoint_Returns_Success()
{
// Arrange
var url = "/api/values";
// Act
var response = await _client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.NotNull(responseString);
}
// Weitere Testmethoden...
}
}
Schritt 5: Ausführen der Tests
Die Tests können mit dem folgenden Befehl ausgeführt werden:
dotnet test
Alternativ kann der Test-Explorer in Visual Studio verwendet werden.
Zusätzliche Hinweise
-
Authentifizierung:
- Wenn die API Authentifizierung verwendet, muss diese für Tests möglicherweise gemockt werden.
- Wenn die API Authentifizierung verwendet, muss diese für Tests möglicherweise gemockt werden.
-
Testdaten-Seeding:
- Vor dem Test wäre die In-Memory-Datenbank mit erforderlichen Testdaten zu befüllen.
- Vor dem Test wäre die In-Memory-Datenbank mit erforderlichen Testdaten zu befüllen.
-
Umgebungsvariablen:
- Ggf. Umgebung auf "Testing" setzen, um spezifische Konfigurationen zu laden (per
ASPNETCORE_ENVIRONMENT
-Umgebungsvariable).
- Ggf. Umgebung auf "Testing" setzen, um spezifische Konfigurationen zu laden (per
Kompletter Beispielcode
CustomWebApplicationFactory.cs
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using YourApiNamespace;
namespace YourApi.Tests
{
public class CustomWebApplicationFactory<TProgram>
: WebApplicationFactory<TProgram> where TProgram : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Entfernen der vorhandenen DbContext-Registrierung
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<YourDbContext>));
if (descriptor != null)
{
services.Remove(descriptor);
}
// Hinzufügen eines In-Memory-Datenbankkontexts
services.AddDbContext<YourDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
// Erstellen des Service Providers
var serviceProvider = services.BuildServiceProvider();
// Initialisieren der Datenbank
using (var scope = serviceProvider.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<YourDbContext>();
db.Database.EnsureCreated();
// Optionale Datenbank-Seeding-Methode
// SeedDatabase(db);
}
});
}
}
}
IntegrationTests.cs
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
using YourApiNamespace;
namespace YourApi.Tests
{
public class IntegrationTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public IntegrationTests(CustomWebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task Get_Endpoint_Returns_Success()
{
// Arrange
var url = "/api/values";
// Act
var response = await _client.GetAsync(url);
// Assert
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.NotNull(responseString);
}
// Weitere Testmethoden...
}
}
Fazit
Integrationstests mittels WebApplicationFactory
und HttpClient
sind eine gute Möglichkeit, die gesamte Anforderung-Antwort-Pipeline zu testen und sicherzustellen, dass alle Komponenten nahtlos zusammenarbeiten.
Weiterführende Links:
Über uns
Ein erfahrenes Entwicklerteam, das mit Leib und Seele Software erstellt.
Letzte Blogeinträge
Einführung: Software-Migration in der IT-Landschaft
Softwaremigration konkret: .NET -> .NET Core
Nützliche Verweise
Kontaktdaten
Brunnstr. 25,
Regensburg
+49 (941) 94592-0
+49 (941) 94592-22