Komponententests für C# in .NET Core mit „dotnet test“ und xUnit

Dieses Tutorial führt Sie interaktiv Schritt für Schritt durch das Erstellen einer Beispielprojektmappe, um die Konzepte von Unittests zu erlernen. Wenn Sie dem Tutorial lieber mit einer vorgefertigten Projektmappe folgen, zeigen Sie den Beispielcode an, oder laden Sie ihn herunter, bevor Sie beginnen. Anweisungen zum Herunterladen finden Sie unter Beispiele und Lernprogramme.

Erstellen des Quellprojekts

Öffnen eines Shell-Fensters. Erstellen Sie ein Verzeichnis namens unit-testing-using-dotnet-test, um die Projektmappe zu speichern. Führen Sie in diesem neuen Verzeichnis dotnet new sln aus, um eine neue Projektmappe zu erstellen. Dies vereinfacht die Verwaltung des Klassenbibliotheks- und Komponententestprojekts. Erstellen Sie im Projektmappenverzeichnis ein PrimeService-Verzeichnis. Nachfolgend wird die bisherige Verzeichnis- und Dateistruktur dargestellt:

/unit-testing-using-dotnet-test
    unit-testing-using-dotnet-test.sln
    /PrimeService

Machen Sie PrimeService zum aktuellen Verzeichnis, und führen Sie dotnet new classlib aus, um das Quellprojekt zu erstellen. Benennen Sie Class1.cs in PrimeService.cs um. Erstellen Sie eine fehlerhafte Implementierung der PrimeService-Klasse, um eine testgesteuerte Entwicklung (Test Driven Development, TDD) zu verwenden:

using System;

namespace Prime.Services
{
    public class PrimeService
    {
        public bool IsPrime(int candidate) 
        {
            throw new NotImplementedException("Please create a test first");
        } 
    }
}

Ändern Sie das Verzeichnis wieder in das Verzeichnis unit-testing-using-dotnet-test. Führen Sie dotnet sln add .\PrimeService\PrimeService.csproj aus, um das Klassenbibliotheksprojekt zur Projektmappe hinzuzufügen.

Erstellen des Testprojekts

Erstellen Sie als Nächstes das Verzeichnis PrimeService.Tests. Die folgende Gliederung zeigt die Verzeichnisstruktur:

/unit-testing-using-dotnet-test
    unit-testing-using-dotnet-test.sln
    /PrimeService
        Source Files
        PrimeService.csproj
    /PrimeService.Tests

Machen Sie das PrimeService.Tests-Verzeichnis zum aktuellen Verzeichnis, und erstellen Sie ein neues Projekt mit dotnet new xunit. Dies erstellt ein Testprojekt, das xUnit als Testbibliothek verwendet. Die generierte Vorlage konfiguriert das Testprogramm in PrimeServiceTests.csproj:

<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0-preview-20170628-02" />
  <PackageReference Include="xunit" Version="2.2.0" />
  <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>

Für das Testprojekt sind weitere Pakete zum Erstellen und Ausführen von Unittests erforderlich. dotnet new hat im vorherigen Schritt xUnit und xUnit Runner hinzugefügt. Fügen Sie jetzt die PrimeService-Klassenbibliothek als eine andere Abhängigkeit zum Projekt hinzu. Verwenden Sie den Befehl dotnet add reference:

dotnet add reference ../PrimeService/PrimeService.csproj

Die ganze Datei finden Sie im Beispielerepository auf GitHub.

Im Folgenden wird das endgültige Projektmappenlayout gezeigt:

/unit-testing-using-dotnet-test
    unit-testing-using-dotnet-test.sln
    /PrimeService
        Source Files
        PrimeService.csproj
    /PrimeService.Tests
        Test Source Files
        PrimeServiceTests.csproj

Führen Sie dotnet sln add .\PrimeService.Tests\PrimeService.Tests.csproj im Verzeichnis unit-testing-using-dotnet-test aus.

Erstellen des ersten Tests

Gemäß dem TDD-Konzept müssen Sie einen fehlerhaften Test schreiben, anschließend dafür sorgen, dass der Test erfolgreich verläuft und dann den Vorgang wiederholen. Entfernen Sie UnitTest1.cs aus dem PrimeService.Tests-Verzeichnis, und erstellen Sie eine neue C#-Datei namens PrimeService_IsPrimeShould.cs. Fügen Sie den folgenden Code hinzu:

using Xunit;
using Prime.Services;

namespace Prime.UnitTests.Services
{
    public class PrimeService_IsPrimeShould
    {
        private readonly PrimeService _primeService;

        public PrimeService_IsPrimeShould()
        {
            _primeService = new PrimeService();
        }

        [Fact]
        public void ReturnFalseGivenValueOf1()
        {
            var result = _primeService.IsPrime(1);

            Assert.False(result, "1 should not be prime");
        }
    }
}

Das [Fact]-Attribut gibt eine Testmethode an, die von Test Runner ausgeführt wird. Führen Sie im Verzeichnis unit-testing-using-dotnet-test dotnet test aus, um die Tests und die Klassenbibliothek zu erstellen und anschließend die Tests auszuführen. Der xUnit Test Runner enthält den Programmeinstiegspunkt zum Ausführen Ihrer Tests. dotnet test startet Test Runner mithilfe des von Ihnen erstellten Komponententestprojekts.

Ihr Test schlägt fehl. Sie haben die Implementierung noch nicht erstellt. Damit dieser Test erfolgreich verläuft, schreiben Sie einen ganz einfachen Code in die PrimeService-Klasse:

public bool IsPrime(int candidate)
{
    if (candidate == 1)
    {
        return false;
    }
    throw new NotImplementedException("Please create a test first");
}

Führen Sie im Verzeichnis unit-testing-using-dotnet-test erneut dotnet test aus. Der dotnet test-Befehl führt einen Build für das PrimeService-Projekt und anschließend für das PrimeService.Tests-Projekt aus. Nachdem beide Projekte erstellt wurden, wird dieser einzelne Test ausgeführt. Er ist erfolgreich.

Hinzufügen weiterer Features

Nachdem Sie dafür gesorgt haben, dass ein Test erfolgreich verläuft, schreiben Sie weiter. Es gibt einige weitere einfache Fälle für Primzahlen: 0, -1. Sie könnten diese neuen Fälle als neue Tests mit dem Attribut [Fact] hinzufügen, aber das wird schnell recht mühsam. Es gibt andere xUnit-Attribute, mit deren Hilfe Sie eine Reihe ähnlicher Tests schreiben können. Ein [Theory]-Attribut stellt eine Reihe von Tests dar, die zwar denselben Code ausführen, jedoch unterschiedliche Eingabeargumente verwenden. Sie können das Attribut [InlineData] verwenden, um Werte für diese Eingaben anzugeben.

Statt neue Tests zu erstellen, wenden Sie diese beiden Attribute zum Erstellen einer einzelnen Theorie an. Bei der Theorie handelt es sich um eine Methode, die mehrere Werte unter zwei als niedrigste Primzahl testet:

[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void ReturnFalseGivenValuesLessThan2(int value)
{
    var result = _primeService.IsPrime(value);
    
    Assert.False(result, $"{value} should not be prime");
}

Führen Sie dotnet test aus und zwei dieser Tests schlagen fehl. Sie müssen die if-Klausel am Anfang der Methode ändern, damit alle Tests erfolgreich sind:

if (candidate < 2)

Wiederholen Sie den Vorgang, indem Sie weitere Tests, Theorien und Code in der Hauptbibliothek hinzufügen. Sie verfügen über die endgültige Version der Tests und die vollständige Implementierung der Bibliothek.

Sie haben eine kleine Bibliothek und eine Reihe von Unittests für diese Bibliothek erstellt. Sie haben die Projektmappe so strukturiert, dass das Hinzufügen neuer Pakete und Tests dem aktuellen Workflows folgt. Sie haben den Großteil Ihrer Zeit und Ihres Aufwands mit der Erreichung der Anwendungsziele verbracht.