Testen in C#: Unit, Integration, E2E von A bis Z

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Warum ist Testen in C# notwendig?

\n

In der modernen Entwicklung mit C# (.NET) ist Codequalität kein Luxus, sondern eine Notwendigkeit. Testen hilft nicht nur, Fehler vor der Auslieferung in die Produktion zu finden, sondern dient auch als lebendige Dokumentation und beschleunigt das Refactoring. Es gibt drei Haupttestebenen, die die „Testpyramide“ bilden: Unit (Komponententests), Integration (Integrationstests) und E2E (End-to-End-Tests). In diesem Artikel werden wir jede dieser Ebenen anhand von realem C#-Codebeispielen behandeln.

\n\n

1. Unit-Testing: Das Fundament der Qualität

\n

Unit-Tests überprüfen ein isoliertes Codefragment (Methode, Klasse) ohne externe Abhängigkeiten (Datenbank, Dateisystem, API). Im .NET-Ökosystem sind drei Frameworks beliebt: xUnit (empfohlen für neue Projekte), NUnit und MSTest.

\n\n

Grundprinzipien (FIRST)

\n
    \n
  • Fast – schnell (Millisekunden).
  • \n
  • Isolated – isoliert (Verwendung von Mocks).
  • \n
  • Repeatable – wiederholbar.
  • \n
  • Self-validating – selbstvalidierend (Assert).
  • \n
  • Timely – zeitnah.
  • \n
\n\n

Beispiel eines Unit-Tests mit xUnit und Moq

\n

Angenommen, wir haben einen Service zur Berechnung von Rabatten:

\n
public interface IDiscountService\n{\n    decimal CalculateDiscount(decimal price, bool isLoyaltyMember);\n}\n\npublic class DiscountService : IDiscountService\n{\n    public decimal CalculateDiscount(decimal price, bool isLoyaltyMember)\n    {\n        if (isLoyaltyMember) return price * 0.9m; // 10% Rabatt\n        return price;\n    }\n}
\n

Test mit Moq (zum Simulieren von Abhängigkeiten) und xUnit:

\n
using Xunit;\nusing Moq;\n\npublic class DiscountServiceTests\n{\n    [Fact]\n    public void CalculateDiscount_WhenLoyaltyMember_ReturnsDiscountedPrice()\n    {\n        // Arrange\n        var service = new DiscountService();\n        var price = 100m;\n\n        // Act\n        var result = service.CalculateDiscount(price, true);\n\n        // Assert\n        Assert.Equal(90m, result);\n    }\n\n    [Theory]\n    [InlineData(100, false, 100)]\n    [InlineData(200, true, 180)]\n    public void CalculateDiscount_MultipleCases_ReturnsCorrectValues(decimal price, bool isLoyalty, decimal expected)\n    {\n        var service = new DiscountService();\n        var result = service.CalculateDiscount(price, isLoyalty);\n        Assert.Equal(expected, result);\n    }\n}
\n

Tipp: Verwenden Sie das Attribut [Fact] für einzelne Tests und [Theory] + [InlineData] für parametrisierte Tests.

\n\n

2. Integrationstests: Überprüfung des Zusammenspiels von Komponenten

\n

Integrationstests prüfen, wie mehrere Module zusammenarbeiten – zum Beispiel ein Controller, ein Service und eine echte Datenbank (oder ein Ersatz, z. B. In-Memory SQLite). In ASP.NET Core wird dafür WebApplicationFactory verwendet.

\n\n

Einrichtung der WebApplicationFactory

\n

Erstellen wir eine Testklasse, die einen Test-Webhost startet:

\n
using Microsoft.AspNetCore.Mvc.Testing;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.DependencyInjection;\nusing MyApp.Data;\n\npublic class CustomWebApplicationFactory : WebApplicationFactory<Program>\n{\n    protected override void ConfigureWebHost(IWebHostBuilder builder)\n    {\n        builder.ConfigureServices(services =>\n        {\n            // Entfernen des echten DbContext\n            var descriptor = services.SingleOrDefault(\n                d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));\n            if (descriptor != null) services.Remove(descriptor);\n\n            // Hinzufügen einer In-Memory-Datenbank\n            services.AddDbContext<AppDbContext>(options =>\n                

Blogs

Buchempfehlungen