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.
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)); }
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();
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
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.
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; }
Na limpeza, o teste precisa concluir o processo:
[TestCleanup()] public void Cleanup() { buildProcess.Close(); }
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>
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";
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(); }
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.