Samouczek: testowanie zadania niestandardowego

Możesz użyć funkcji testowania jednostkowego w programie Visual Studio, aby przetestować niestandardowe zadanie MSBuild przed dystrybucją, aby zapewnić poprawność kodu. Aby uzyskać informacje o zaletach wykonywania testów i podstawowych narzędzi testowych, zobacz podstawowe informacje na temat testów jednostkowych. W tym samouczku użyto przykładów kodu używanych w innych samouczkach zadań niestandardowych programu MSBuild. Następujące projekty używane w tych samouczkach są dostępne w usłudze GitHub i obejmują testy jednostkowe i integracyjne dla zadań niestandardowych programu MSBuild:

Test jednostkowy

Zadanie niestandardowe programu MSBuild to klasa dziedziczona bezpośrednio Task lub pośrednio, ponieważ ToolTask dziedziczy z Taskklasy ). Metoda wykonująca akcje skojarzone z zadaniem to Execute(). Ta metoda przyjmuje niektóre wartości wejściowe (parametry) i ma parametry wyjściowe, których można użyć do testowania poprawności. W tym przypadku niektóre parametry wejściowe są ścieżkami do plików, więc w tym przykładzie znajdują się pliki wejściowe testowe w folderze o nazwie Resources. To zadanie MSBuild generuje również pliki, więc test potwierdza wygenerowane pliki.

Wymagany jest aparat kompilacji, który jest klasą implementjącą IBuildEngine. W tym przykładzie istnieje pozorowanie przy użyciu narzędzia Moq, ale można użyć innych pozornych narzędzi. Przykład zbiera błędy, ale możesz zebrać inne informacje, a następnie je potwierdzić.

Pozorowanie Engine jest wymagane we wszystkich testach, więc jest uwzględniane jako TestInitialize (jest wykonywane przed każdym testem, a każdy test ma własny aparat kompilacji).

Pełny kod można znaleźć w pliku AppSettingStronglyTypedTest.cs w repozytorium przykładów platformy .NET w witrynie GitHub.

  1. Utwórz zadanie i ustaw parametry w ramach układu testu:

        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. Utwórz pozorny ITaskItem parametr (przy użyciu narzędzia Moq) i wskaż plik, który ma zostać przeanalizowany. Następnie utwórz AppSettingStronglyTyped zadanie niestandardowe z jego parametrami. Na koniec ustaw aparat kompilacji na zadanie niestandardowe 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;
    

    Następnie wykonaj kod zadania, aby wykonać rzeczywistą akcję zadania:

     //Act
     var success = appSettingStronglyTyped.Execute();
    
  3. Na koniec należy potwierdzić oczekiwany wynik testu:

    //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. Pozostałe testy są zgodne z tym wzorcem i rozszerzają wszystkie możliwości.

Uwaga

W przypadku wygenerowania plików należy użyć innej nazwy pliku dla każdego testu, aby uniknąć kolizji. Pamiętaj, aby usunąć wygenerowane pliki jako oczyszczanie testów.

Testy integracji

Testy jednostkowe są ważne, ale należy również przetestować niestandardowe zadanie MSBuild w realistycznym kontekście kompilacji.

Klasa System.Diagnostics.Process zapewnia dostęp do procesów lokalnych i zdalnych oraz umożliwia uruchamianie i zatrzymywanie lokalnych procesów systemowych. W tym przykładzie jest uruchamiana kompilacja w teście jednostkowym przy użyciu testowych plików MSBuild.

  1. Kod testowy musi zainicjować kontekst wykonywania dla każdego testu. Zwróć uwagę, aby upewnić się, że ścieżka do dotnet polecenia jest dokładna dla danego środowiska. Kompletny przykład znajduje się tutaj.

         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. Po oczyszczeniu test musi zakończyć proces:

        [TestCleanup()]
         public void Cleanup()
         {
             buildProcess.Close();
         }
    
  3. Teraz utwórz każdy test. Do wykonania każdego testu będzie potrzebna własna definicja pliku MSBuild. Na przykład testscript-success.msbuild. Aby zrozumieć ten plik, zobacz Samouczek: Kreta niestandardowe zadanie.

    <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. Argument testowy zawiera instrukcje dotyczące kompilowania tego pliku MSBuild:

     //Arrange
     buildProcess.StartInfo.Arguments = "build .\\Resources\\testscript-success.msbuild /t:generateSettingClass";
    
  5. Wykonaj i pobierz dane wyjściowe:

    //Act
    ExecuteCommandAndCollectResults();
    

    Gdzie ExecuteCommandAndCollectResults() jest definiowany jako:

    private void ExecuteCommandAndCollectResults()
    {
         buildProcess.Start();
         while (!buildProcess.StandardOutput.EndOfStream)
         {
             output.Add(buildProcess.StandardOutput.ReadLine() ?? string.Empty);
         }
         buildProcess.WaitForExit();
    }
    
  6. Na koniec oceń oczekiwany wynik:

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

Podsumowanie

Testowanie jednostkowe jest przydatne, ponieważ można testować i debugować kod w celu zapewnienia poprawności każdego konkretnego fragmentu kodu, ale posiadanie testów integracji jest ważne, aby upewnić się, że zadanie jest wykonywane w realistycznym kontekście kompilacji. W tym samouczku przedstawiono sposób testowania niestandardowego zadania MSBuild.

Następne kroki

Utwórz bardziej złożone zadanie niestandardowe, które wykonuje generowanie kodu interfejsu API REST.