Tutorial: Testen eines benutzerdefinierten Tasks

Sie können Komponententestfunktionen in Visual Studio verwenden, um einen benutzerdefinierten MSBuild-Task vor dem Verteilen zu testen, um die Richtigkeit des Codes sicherzustellen. Informationen zu den Vorteilen von Tests und grundlegenden Testtools finden Sie unter Grundlagen zu Komponententests. In diesem Tutorial verwenden Sie die Codebeispiele, die in anderen Tutorials zu benutzerdefinierten MSBuild-Tasks verwendet werden. Die folgenden Projekte, die in diesen Tutorials verwendet werden, sind auf GitHub verfügbar und enthalten Komponenten- und Integrationstests für MSBuild-Tasks:

Komponententest

Ein benutzerdefinierter MSBuild-Task ist eine Klasse, die von Task erbt (direkt oder indirekt, weil ToolTask von Task erbt). Die Methode, die die mit dem Task verbundenen Aktionen durchführt, ist Execute(). Diese Methode nimmt einige Eingabewerte (Parameter) an und verfügt über Ausgabeparameter, die Sie mit „assert“ auf ihre Gültigkeit prüfen können. In diesem Fall sind einige Eingabeparameter Pfade zu Dateien, sodass in diesem Beispiel Testeingabedateien in einem Ordner namens Resources enthalten sind. Dieser MSBuild-Task generiert auch Dateien, daher bestätigt der Test die generierten Dateien.

Eine Build-Engine ist erforderlich, bei der es sich um eine Klasse handelt, die IBuildEngine implementiert. In diesem Beispiel wird Mocking (Moq) verwendet, aber Sie können auch andere Mockingtools verwenden. Das Beispiel erfasst die Fehler, aber Sie können auch andere Informationen erfassen und diese dann bestätigen.

Das Pseudoobjekt Engine wird für alle Tests benötigt, daher ist es als TestInitialize eingebunden (es wird vor jedem Test ausgeführt, und jeder Test hat eine eigene Build-Engine).

Den vollständigen Code finden Sie unter AppSettingStronglyTypedTest.cs im .NET-Beispielrepository auf GitHub.

  1. Erstellen Sie den Task, und legen Sie die Parameter als Teil der Testanordnung fest:

        private Mock<IBuildEngine> buildEngine;
        private List<BuildErrorEventArgs> errors;
    
         [TestInitialize()]
         public void Startup()
         {
             buildEngine = new Mock<IBuildEngine>();
             errors = new List<BuildErrorEventArgs>();
             buildEngine.Setup(x => x.LogErrorEvent(It.IsAny<BuildErrorEventArgs>())).Callback<BuildErrorEventArgs>(e => errors.Add(e));
         }
    
  2. Erstellen Sie das ITaskItem-Parameterpseudoobjekt (mit Moq), und verweisen Sie auf die zu analysierende Datei. Erstellen Sie dann den benutzerdefinierten AppSettingStronglyTyped-Task mit seinen Parametern. Legen Sie abschließend die Build-Engine auf den benutzerdefinierten MSBuild-Task fest:

    //Arrange
    var item = new Mock<ITaskItem>();
    item.Setup(x => x.GetMetadata("Identity")).Returns($".\\Resources\\complete-prop.setting");
    
    var appSettingStronglyTyped = new AppSettingStronglyTyped { SettingClassName = "MyCompletePropSetting", SettingNamespaceName = "MyNamespace", SettingFiles = new[] { item.Object } };
    
    appSettingStronglyTyped.BuildEngine = buildEngine.Object;
    

    Führen Sie dann den Taskcode aus, um die eigentliche Taskaktion auszuführen:

     //Act
     var success = appSettingStronglyTyped.Execute();
    
  3. Abschließend können Sie das erwartete Ergebnis des Tests bestätigen:

    //Assert
    Assert.IsTrue(success); // The execution was success
    Assert.AreEqual(errors.Count, 0); //Not error were found
    Assert.AreEqual($"MyCompletePropSetting.generated.cs", appSettingStronglyTyped.ClassNameFile); // The Task expected output
    Assert.AreEqual(true, File.Exists(appSettingStronglyTyped.ClassNameFile)); // The file was generated
    Assert.IsTrue(File.ReadLines(appSettingStronglyTyped.ClassNameFile).SequenceEqual(File.ReadLines(".\\Resources\\complete-prop-class.txt"))); // Assenting the file content
    
  4. Die anderen Tests folgen diesem Muster und erweitern alle Möglichkeiten.

Hinweis

Wenn Dateien generiert werden, müssen Sie für jeden Test einen anderen Dateinamen verwenden, um Konflikte zu vermeiden. Denken Sie daran, die generierten Dateien bei der Testbereinigung zu löschen.

Integrationstests

Komponententests sind wichtig, aber Sie müssen auch den benutzerdefinierten MSBuild-Task in einem realistischen Buildkontext testen.

System.Diagnostics.Process Class ermöglicht Zugriff auf lokale Prozesse und Remoteprozesse und das Starten und Beenden lokaler Systemprozesse. In diesem Beispiel wird ein Build für einen Komponententest mithilfe von MSBuild-Testdateien ausgeführt.

  1. Der Testcode muss den Ausführungskontext für jeden Test initialisieren. Achten Sie darauf, dass der Pfad zum dotnet-Befehl für Ihre Umgebung richtig ist. Das vollständige Beispiel finden Sie hier.

         public const string MSBUILD = "C:\\Program Files\\dotnet\\dotnet.exe";
    
         private Process buildProcess;
         private List<string> output;
    
         [TestInitialize()]
         public void Startup()
         {
             output = new List<string>();
             buildProcess = new Process();
             buildProcess.StartInfo.FileName = MSBUILD;
             buildProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
             buildProcess.StartInfo.CreateNoWindow = true;
             buildProcess.StartInfo.RedirectStandardOutput = true;
         }
    
  2. Bei der Bereinigung muss der Test den Prozess abschließen:

        [TestCleanup()]
         public void Cleanup()
         {
             buildProcess.Close();
         }
    
  3. Erstellen Sie nun jeden einzelnen Test. Jeder Test benötigt eine eigene MSBuild-Dateidefinition, um ausgeführt zu werden. Beispiel: testscript-success.msbuild. Informationen zur Datei finden Sie unter Tutorial: Erstellen eines benutzerdefinierten Tasks.

    <Project Sdk="Microsoft.NET.Sdk">
        <UsingTask TaskName="AppSettingStronglyTyped.AppSettingStronglyTyped" AssemblyFile="..\AppSettingStronglyTyped.dll" />
        <PropertyGroup>
            <TargetFramework>netstandard2.1</TargetFramework>
        </PropertyGroup>
    
        <PropertyGroup>
            <SettingClass>MySettingSuccess</SettingClass>
            <SettingNamespace>example</SettingNamespace>
        </PropertyGroup>
    
        <ItemGroup>
            <SettingFiles Include="complete-prop.setting" />
        </ItemGroup>
    
        <Target Name="generateSettingClass">
            <AppSettingStronglyTyped SettingClassName="$(SettingClass)" SettingNamespaceName="$(SettingNamespace)" SettingFiles="@(SettingFiles)">
                <Output TaskParameter="ClassNameFile" PropertyName="SettingClassFileName" />
            </AppSettingStronglyTyped>
        </Target>
    </Project>
    
  4. Das Testargument enthält die Anweisungen zum Erstellen dieser MSBuild-Datei:

     //Arrange
     buildProcess.StartInfo.Arguments = "build .\\Resources\\testscript-success.msbuild /t:generateSettingClass";
    
  5. Führen Sie die Datei aus, und rufen Sie die Ausgabe ab:

    //Act
    ExecuteCommandAndCollectResults();
    

    Dabei wird ExecuteCommandAndCollectResults() wie folgt definiert:

    private void ExecuteCommandAndCollectResults()
    {
         buildProcess.Start();
         while (!buildProcess.StandardOutput.EndOfStream)
         {
             output.Add(buildProcess.StandardOutput.ReadLine() ?? string.Empty);
         }
         buildProcess.WaitForExit();
    }
    
  6. Bestätigen Sie abschließend das erwartete Ergebnis:

    //Assert
    Assert.AreEqual(0, buildProcess.ExitCode); //Finished success
    Assert.IsTrue(File.Exists(".\\Resources\\MySettingSuccess.generated.cs")); // the expected resource was generated
    Assert.IsTrue(File.ReadLines(".\\Resources\\MySettingSuccess.generated.cs").SequenceEqual(File.ReadLines(".\\Resources\\testscript-success-class.txt"))); // asserting the file content
    

Schlussbemerkung

Komponententests sind nützlich, da Sie den Code testen und debuggen können, um die Richtigkeit jedes einzelnen Codeabschnitts sicherzustellen. Integrationstests sind jedoch wichtig, um sicherzustellen, dass der Task in einem realistischen Buildkontext ausgeführt wird. In diesem Tutorial haben Sie gelernt, wie Sie einen benutzerdefinierten MSBuild-Task testen.

Nächste Schritte

Erstellen Sie einen komplexeren benutzerdefinierten Task, der die REST-API-Codegenerierung übernimmt.