Partager via


Tutoriel : Tester une tâche personnalisée

Vous pouvez utiliser la fonctionnalité de test unitaire dans Visual Studio pour tester une tâche personnalisée MSBuild avant la distribution afin de garantir l’exactitude du code. Pour plus d’informations sur les avantages des tests et des outils de test de base, consultez Principes de base sur les tests unitaires. Dans ce tutoriel, vous utilisez les exemples de code utilisés dans d’autres tutoriels de tâches personnalisées MSBuild. Les projets suivants utilisés dans ces tutoriels sont disponibles dans GitHub et incluent des tests unitaires et d’intégration pour les tâches personnalisées MSBuild :

Test unitaire

Une tâche personnalisée MSBuild est une classe qui hérite de Task (directement ou indirectement, car ToolTask hérite de Task). La méthode qui effectue les actions associées à la tâche est Execute(). Cette méthode prend certaines valeurs d’entrée (paramètres) et possède des paramètres de sortie que vous pouvez utiliser pour tester la validité. Dans ce cas, certains paramètres d’entrée étant des chemins d’accès aux fichiers, cet exemple contient des fichiers d’entrée de test dans un dossier appelé Resources. Cette tâche MSBuild génère également des fichiers, de sorte que le test affirme les fichiers générés.

Un moteur de build est nécessaire ; il s’agit d’une classe qui implémente IBuildEngine. Dans cet exemple, vous avez un objet fictif qui utilise Moq, mais vous pouvez utiliser d’autres outils fictifs. L’exemple collecte les erreurs, mais vous pouvez collecter d’autres informations, puis les affirmer.

La simulation Engine est nécessaire sur tous les tests ; elle est donc incluse comme TestInitialize (elle est exécutée avant chaque test, et chaque test a son propre moteur de génération).

Pour obtenir le code complet, consultez AppSettingStronglyTypedTest.cs dans le référentiel d’exemples .NET sur GitHub.

  1. Créez la tâche et définissez les paramètres dans le cadre de l’arrangement de test :

        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. Créez l’objet fictif de paramètre ITaskItem (à l’aide de Moq) et pointez sur le fichier à analyser. Ensuite, créez la tâche personnalisée AppSettingStronglyTyped avec ses paramètres. Enfin, définissez le moteur de build sur la tâche personnalisée MSBuild :

    //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;
    

    Ensuite, exécutez le code de tâche pour effectuer l’action de tâche réelle :

     //Act
     var success = appSettingStronglyTyped.Execute();
    
  3. Enfin, affirmez le résultat attendu du test :

    //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. Les autres tests suivent ce modèle et étendent toutes les possibilités.

Notes

Quand des fichiers sont générés, vous devez utiliser un nom de fichier différent pour chaque test afin d’éviter toute collision. N’oubliez pas de supprimer les fichiers générés en tant que nettoyage de test.

Tests d’intégration

Les tests unitaires sont importants, mais vous devez également tester la tâche MSBuild personnalisée dans un contexte de build réaliste.

La classe System.Diagnostics.Process fournit l’accès à des processus locaux ainsi que distants, et vous permet de démarrer et d’arrêter des processus système locaux. Cet exemple exécute une build sur un test unitaire à l’aide de fichiers MSBuild de test.

  1. Le code de test doit initialiser le contexte d’exécution pour chaque test. Veillez à ce que le chemin d’accès à la commande dotnet soit exact pour votre environnement. L’exemple complet se trouve ici.

         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. Lors du nettoyage, le test doit terminer le processus :

        [TestCleanup()]
         public void Cleanup()
         {
             buildProcess.Close();
         }
    
  3. Maintenant, créez chaque test. Chaque test a besoin de sa propre définition de fichier MSBuild pour être exécuté. Par exemple testscript-success.msbuild. Pour comprendre le fichier, consultez Tutoriel : Créer une tâche personnalisée.

    <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. L’argument de test donne les instructions pour générer ce fichier MSBuild :

     //Arrange
     buildProcess.StartInfo.Arguments = "build .\\Resources\\testscript-success.msbuild /t:generateSettingClass";
    
  5. Exécutez et obtenez la sortie :

    //Act
    ExecuteCommandAndCollectResults();
    

    ExecuteCommandAndCollectResults() est défini comme :

    private void ExecuteCommandAndCollectResults()
    {
         buildProcess.Start();
         while (!buildProcess.StandardOutput.EndOfStream)
         {
             output.Add(buildProcess.StandardOutput.ReadLine() ?? string.Empty);
         }
         buildProcess.WaitForExit();
    }
    
  6. Enfin, évaluez le résultat attendu :

    //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
    

Conclusion

Les tests unitaires sont utiles, car vous pouvez tester et déboguer le code pour garantir l’exactitude de chaque élément de code spécifique, mais il est important d’avoir des tests d’intégration pour vous assurer que la tâche s’exécute dans un contexte de génération réaliste. Dans ce tutoriel, vous avez appris à tester une tâche personnalisée MSBuild.

Étapes suivantes

Créez une tâche personnalisée plus complexe qui génère du code d’API REST.