Compartilhar via


Tutorial: testar uma tarefa personalizada

Você pode usar a funcionalidade de teste de unidade no Visual Studio para testar uma tarefa personalizada do MSBuild antes da distribuição para garantir a exatidão do código. Para obter informações sobre os benefícios de fazer testes e ferramentas básicas de teste, consulte noções básicas sobre testes de unidade. Nesse tutorial, você usará os exemplos de código usados em outros tutoriais de tarefas personalizadas do MSBuild. Os seguintes projetos usados nesses tutoriais estão disponíveis no GitHub e incluem testes de unidade e integração para tarefas personalizadas do MSBuild:

Teste de unidade

Uma tarefa personalizada do MSBuild é uma classe que herda de Task (direta ou indiretamente, porque ToolTask herda de Task). O método que executa as ações associadas à tarefa é Execute(). Esse método usa alguns valores de entrada (parâmetros) e tem parâmetros de saída que você pode usar afirmação para testar a validade. Nesse caso, alguns parâmetros de entrada são caminhos para arquivos, portanto, este exemplo tem arquivos de entrada de teste em uma pasta chamada Recursos. Essa tarefa do MSBuild também gera arquivos, portanto, o teste afirma os arquivos gerados.

Um mecanismo de compilação é necessário, que é uma classe que implementa IBuildEngine. Nesse exemplo, há uma simulação usando o Moq, mas você pode usar outras ferramentas de simulação. O exemplo coleta os erros, mas você pode coletar outras informações e, em seguida, afirmá-las.

A simulação Engine é necessária em todos os testes, portanto, ela é incluída como TestInitialize (ela é executada antes de cada teste e cada teste tem o próprio mecanismo de compilação).

Para obter o código completo, consulte AppSettingStronglyTypedTest.cs no repositório de amostras do .NET no GitHub.

  1. Crie a tarefa e defina os parâmetros como parte da organização do teste:

        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. Crie a simulação de parâmetro ITaskItem (usando Moq) e aponte para o arquivo a ser analisado. Em seguida, crie a tarefa personalizada AppSettingStronglyTyped com seus parâmetros. Por fim, defina o mecanismo de compilação para a tarefa personalizada do 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;
    

    Em seguida, execute o código da tarefa para executar a ação de tarefa real:

     //Act
     var success = appSettingStronglyTyped.Execute();
    
  3. Por fim, afirme o resultado esperado do teste:

    //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. Os outros testes seguem esse padrão e expandem todas as possibilidades.

Observação

Quando há arquivos gerados, você precisa usar um nome de arquivo diferente para cada teste para evitar colisão. Lembre-se de excluir os arquivos gerados como limpeza de teste.

Testes de integração

Os testes de unidade são importantes, mas você também precisa testar a tarefa personalizada do MSBuild em um contexto de compilação realista.

A classe System.Diagnostics.Process fornece acesso a processos locais e remotos e permite que você inicie e pare os processos do sistema local. Esse exemplo executa uma compilação em um teste de unidade usando arquivos de teste do MSBuild.

  1. O código de teste precisa inicializar o contexto de execução para cada teste. Preste atenção para garantir que o caminho para o comando dotnet seja preciso para seu ambiente. O exemplo completo está aqui.

         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. Na limpeza, o teste precisa concluir o processo:

        [TestCleanup()]
         public void Cleanup()
         {
             buildProcess.Close();
         }
    
  3. Agora, crie cada teste. Cada teste precisará que sua própria definição de arquivo do MSBuild seja executada. Por exemplo, testscript-success.msbuild. Para entender o arquivo, consulte Tutorial: criar uma tarefa 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. O argumento de teste fornece as instruções para compilar este arquivo do MSBuild:

     //Arrange
     buildProcess.StartInfo.Arguments = "build .\\Resources\\testscript-success.msbuild /t:generateSettingClass";
    
  5. Execute e obtenha a saída:

    //Act
    ExecuteCommandAndCollectResults();
    

    Sendo ExecuteCommandAndCollectResults() definido como:

    private void ExecuteCommandAndCollectResults()
    {
         buildProcess.Start();
         while (!buildProcess.StandardOutput.EndOfStream)
         {
             output.Add(buildProcess.StandardOutput.ReadLine() ?? string.Empty);
         }
         buildProcess.WaitForExit();
    }
    
  6. Por fim, avalie o 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
    

Conclusão

O teste de unidade é útil porque você pode testar e depurar o código para garantir a exatidão de cada parte específica do código, mas ter testes de integração é importante para garantir que a tarefa seja executada em um contexto de compilação realista. Nesse tutorial, você aprendeu a testar uma tarefa personalizada do MSBuild.

Próximas etapas

Crie uma tarefa personalizada mais complexa que faz a geração de código da API REST.