Tutorial: Prueba de una tarea personalizada

Puede usar la funcionalidad de pruebas unitarias en Visual Studio para probar una tarea personalizada de MSBuild antes de la distribución, a fin de garantizar la precisión del código. Para obtener información sobre las ventajas de realizar pruebas y las herramientas de prueba básicas, vea los conceptos básicos sobre las pruebas unitarias. En este tutorial, usará los ejemplos de código utilizados en otros tutoriales de tareas personalizadas de MSBuild. Los siguientes proyectos usados en esos tutoriales están disponibles en GitHub e incluyen pruebas unitarias y de integración para tareas personalizadas de MSBuild:

Prueba unitaria

Una tarea personalizada de MSBuild es una clase que hereda de Task (directa o indirectamente, porque ToolTask hereda de Task). El método que realiza las acciones asociadas a la tarea es Execute(). Este método adopta algunos valores de entrada (parámetros) y tiene parámetros de salida con los que puede usar assert para probar la validez. En este caso, algunos parámetros de entrada son rutas de acceso a archivos, por lo que este ejemplo tiene archivos de entrada de prueba en una carpeta denominada Recursos. Esta tarea de MSBuild también genera archivos, por lo que la prueba declara los archivos generados.

Se necesita un motor de compilación, que es una clase que implementa IBuildEngine. En este ejemplo, hay un objeto ficticio con Moq, pero puede usar otras herramientas de simulación. En el ejemplo se recopilan los errores, pero puede recopilar otra información y, a continuación, declararla.

El objeto ficticio Engine es necesario en todas las pruebas, por lo que se incluye como TestInitialize (se ejecuta antes de cada prueba, y cada prueba tiene su propio motor de compilación).

Para obtener el código completo, vea AppSettingStronglyTypedTest.cs en el repositorio de ejemplos de .NET en GitHub.

  1. Cree la tarea y establezca los parámetros como parte de la organización de la prueba:

        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. Cree el objeto ficticio de parámetros ITaskItem (mediante Moq) y apunte al archivo que se va a analizar. A continuación, cree la tarea personalizada AppSettingStronglyTyped con sus parámetros. Por último, configure el motor de compilación para la tarea personalizada de 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;
    

    Después, ejecute el código de la tarea para realizar la acción de la tarea real:

     //Act
     var success = appSettingStronglyTyped.Execute();
    
  3. Por último, declare el resultado esperado de la prueba:

    //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. Las demás pruebas siguen este patrón y amplían todas las posibilidades.

Nota

Cuando se generan archivos, debe usar un nombre de archivo diferente para cada prueba a fin de evitar conflictos. No olvide eliminar los archivos generados como una limpieza de prueba.

Pruebas de integración

Las pruebas unitarias son importantes, pero también debe probar la tarea personalizada de MSBuild en un contexto de compilación realista.

La clase System.Diagnostics.Process proporciona acceso a procesos locales y remotos y permite iniciar y detener procesos del sistema local. En este ejemplo se ejecuta una compilación en una prueba unitaria mediante archivos de MSBuild de prueba.

  1. El código de prueba debe inicializar el contexto de ejecución para cada prueba. Preste atención para asegurarse de que la ruta de acceso al comando dotnet es precisa para su entorno. El ejemplo completo se encuentra aquí.

         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. Al realizar la limpieza, la prueba debe finalizar el proceso:

        [TestCleanup()]
         public void Cleanup()
         {
             buildProcess.Close();
         }
    
  3. Ahora, cree cada prueba. Cada prueba necesitará su propia definición de archivo de MSBuild que se va a ejecutar. Por ejemplo, testscript-success.msbuild. Para entender el archivo, vea Tutorial: Creación de una tarea personalizada.

    <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. El argumento de prueba proporciona las instrucciones para compilar este archivo de MSBuild:

     //Arrange
     buildProcess.StartInfo.Arguments = "build .\\Resources\\testscript-success.msbuild /t:generateSettingClass";
    
  5. Ejecute y obtenga la salida:

    //Act
    ExecuteCommandAndCollectResults();
    

    Donde ExecuteCommandAndCollectResults() se define como:

    private void ExecuteCommandAndCollectResults()
    {
         buildProcess.Start();
         while (!buildProcess.StandardOutput.EndOfStream)
         {
             output.Add(buildProcess.StandardOutput.ReadLine() ?? string.Empty);
         }
         buildProcess.WaitForExit();
    }
    
  6. Por último, evalúe el resultado esperado:

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

Conclusión

Las pruebas unitarias son útiles porque puede probar y depurar el código para garantizar la precisión de cada fragmento de código específico, pero es importante tener pruebas de integración para asegurarse de que la tarea se ejecuta en un contexto de compilación realista. En este tutorial, ha aprendido a probar una tarea personalizada de MSBuild.

Pasos siguientes

Cree una tarea personalizada más compleja que se encargue de generar el código de API de REST.