Microsoft. Możliwości Testing.Platform (MTP)

W kontekście MTP funkcja odnosi się do potencjalnego wykonania konkretnej akcji lub dostarczenia określonych informacji. Jest to środek dla frameworku testowego i rozszerzeń, aby zadeklarować swoją zdolność do działania w określony sposób lub udostępniania określonych informacji żądającym.

Żądający mogą być dowolnym składnikiem biorącym udział w sesji testowej, takim jak platforma, rozszerzenie lub framework testowy.

Głównym celem systemu możliwości jest ułatwienie efektywnej komunikacji między składnikami biorącymi udział w sesji testowej, umożliwiając im wymianę informacji i dokładne spełnienie ich odpowiednich potrzeb.

Przykład z przewodnikiem

Rozważmy hipotetyczny przykład, aby zademonstrować konieczność systemu możliwości.

Notatka

Ten przykład jest wyłącznie w celach ilustracyjnych i nie jest obecnie implementowany w MTP ani w żadnej strukturze testowania.

Wyobraź sobie sytuację, w której masz rozszerzenie, które wymaga platformy testowania do wykonania nie więcej niż jednego testu naraz. Ponadto po każdym teście rozszerzenie musi znać użycie procesora CPU dla tego konkretnego testu.

Aby uwzględnić powyższy scenariusz, należy zapytać się z platformy testowania, jeśli:

  1. Ma możliwość wykonywania tylko jednego testu jednocześnie.
  2. Może zapewniać informacje o ilości użycia CPU przez każdy test.

Jak rozszerzenie może określić, czy platforma testowania ma możliwość działania w tym trybie i udostępnić informacje o użyciu procesora CPU dla sesji testowej? W MTP ta funkcja jest reprezentowana przez implementację interfejsu Microsoft.Testing.Platform.Capabilities.ICapability:

// Base capabilities contracts

public interface ICapability
{
}

public interface ICapabilities<TCapability>
    where TCapability : ICapability
{
    IReadOnlyCollection<TCapability> Capabilities { get; }
}

// Specific testing framework capabilities

public interface ITestFrameworkCapabilities : ICapabilities<ITestFrameworkCapability>
{
}

public interface ITestFrameworkCapability : ICapability
{
}

Jak widać, interfejs ICapability jest interfejsem markera, ponieważ może reprezentować dowolną możliwość, a rzeczywista implementacja będzie zależna od kontekstu. Zobaczysz również ITestFrameworkCapability, który dziedziczy z ICapability w celu sklasyfikowania możliwości. Ogólny charakter systemu możliwości umożliwia wygodne grupowanie według kontekstu. ITestFrameworkCapability grupuje wszystkie możliwości zaimplementowane przez platformę testowania . Interfejs ICapabilities<TCapability> ujawnia zestaw wszystkich funkcji implementowanych przez rozszerzenie. Podobnie w przypadku podstawowego istnieje struktura testowania specyficzna dla kontekstu o nazwie ITestFrameworkCapabilities. ITestFrameworkCapabilities jest dostarczana do platformy podczas procesu rejestracji platformy testowania.

Aby utworzyć możliwość, która dotyczy wyżej wymienionego scenariusza, zdefiniuj ją w następujący sposób:

public interface IDisableParallelismCapability : ITestFrameworkCapability
{
    bool CanDisableParallelism { get; }
    bool CanProvidePerTestCpuConsumption { get; }
    void Enable();
}

Jeśli platforma testowania implementuje ten interfejs, w czasie wykonywania można wykonywać następujące zapytania:

  • Sprawdź, czy platforma testowania ma możliwość wyłączenia równoległości CanDisableParallelism = true.
  • Ustal, czy platforma testowania może dostarczyć dane użycia procesora CPU CanProvidePerTestCPUConsumption = true.
  • Zażądaj adaptera testowego, aby aktywować ten tryb, wywołując metodę Enable() przed rozpoczęciem sesji testowej.

Hipotetyczny fragment kodu wewnątrz rozszerzenia może wyglądać następująco:

IServiceProvider serviceProvider = null; // TODO: Get IServiceProvider.
var capabilities = serviceProvider.GetRequiredService<ITestFrameworkCapabilities>();

// Utilize the `GetCapability` API to search for the specific capability to query.
var capability = capabilities.GetCapability<IDisableParallelismCapability>();
if (capability is null)
{
    // Capability not supported...
}
else
{
    capability.Enable();
    if (capability.CanDisableParallelism)
    {
        // Do something...
    }

    if (capability.CanProvidePerTestCpuConsumption)
    {
        // Do something...
    }
}

W poprzednim przykładzie pokazano, w jaki sposób infrastruktura możliwości umożliwia zaawansowany mechanizm komunikacji między składnikami biorącymi udział w sesji testowej. Chociaż w przykładzie pokazano możliwość specjalnie zaprojektowaną dla frameworku testowego, każdy składnik może uwidaczniać i implementować rozszerzenia dziedziczone z ICapability.

Widać, że nie wszystkie szczegóły mogą być przekazywane za pośrednictwem interfejsu. Biorąc pod uwagę poprzedni przykład, jakiego rozszerzenia należy oczekiwać, jeśli CanProvidePerTestCpuConsumption jest obsługiwana? Jakiego rodzaju informacje niestandardowe mają być przesyłane za pośrednictwem IMessageBus przez platformę testowania? Rozwiązanie to dokumentacja zdolności. Jest to odpowiedzialność właściciela funkcjonalności , aby projektować, wdrażać i dokumentować ją jak najjaśniej, aby pomóc implementatorom chcącym skutecznie współpracować z rozszerzeniem, które wymaga tej konkretnej funkcjonalności.

Na przykład rozszerzenie raportu TRX umożliwia platformie testowania zaimplementowanie niezbędnej możliwości dokładnego wygenerowania raportu TRX. Rozszerzenie służące do rejestracji jest zawarte w pakiecie NuGet https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport, ale możliwość implementowania znajduje się w pakiecie zawierającym wyłącznie kontrakty.

Podsumowując, podsumujemy podstawowe aspekty systemu możliwości:

  • Niezbędne jest ułatwienie jasnej i stabilnej komunikacji między składnikami.
  • Wszystkie możliwości powinny dziedziczyć z ICapability lub interfejsu dziedziczonego po nim i są udostępniane za pośrednictwem kolekcji za pomocą interfejsu ICapabilities.
  • Ułatwia to ewolucję funkcji bez powodowania zmian powodujących niezgodność. Jeśli określona funkcja nie jest obsługiwana, można podjąć odpowiednie działania.
  • Na właścicielu możliwości spoczywa odpowiedzialność za projektowanie, wysyłkę i dokumentowanie użycia danej funkcji. Protokół MTP może być również właścicielem możliwości w taki sam sposób, jak w przypadku każdego innego rozszerzenia.

Możliwości ram

Platforma uwidacznia wyspecjalizowany interfejs o nazwie ITestFrameworkCapability, który jest podstawą wszystkich możliwości udostępnianych dla platform testowych. Te możliwości są udostępniane podczas rejestrowania frameworku testowego na platformie.

IBannerMessageOwnerCapability

Opcjonalna możliwość struktury testowej pozwala strukturze testowej przekazywać platformie komunikat banera. Jeśli komunikat to null lub funkcja nie jest obecna, platforma używa domyślnego komunikatu baneru.

Ta implementacja możliwości pozwala na abstrahowanie różnych warunków, które framework testowy musi rozważyć podczas decydowania, czy komunikat banera powinien być wyświetlany.

Platforma udostępnia IPlatformInformation usługę, aby dostarczyć pewne informacje o platformie, które mogą być przydatne podczas tworzenia niestandardowego komunikatu baneru.

ITrxReportCapability

Aby dostarczyć metadane specyficzne dla struktury do rozszerzenia raportu TRX, zaimplementuj element Microsoft.Testing.Extensions.TrxReport.Abstractions.ITrxReportCapability. Odwołaj się do pakietu Microsoft.Testing.Extensions.TrxReport.Abstractions w swoim frameworku. Aplikacja testowa wymaga również pakietu Microsoft.Testing.Extensions.TrxReport do tworzenia raportów.

Ta możliwość oddziela obsługę od aktywacji:

  • Zwróć true z IsSupported, gdy platforma może dostarczyć metadane TRX.
  • W Enable() zarejestruj, że reporter TRX zażądał metadanych. Reporter wywołuje tę metodę na początku sesji testowej, gdy raporty TRX są włączone.
  • Po aktywacji dodaj właściwości TRX do węzłów testowych publikowanych przez strukturę.

Poniższa możliwość przechowuje stan aktywacji. Przykładowe projekty są przeznaczone dla platformy .NET 10 i odwołują się do MTP 2.5.1:

internal sealed class TrxCapability : ITrxReportCapability
{
    public bool IsSupported => true;
    public bool IsEnabled { get; private set; }

    public void Enable() => IsEnabled = true;
}
Friend NotInheritable Class TrxCapability
    Implements ITrxReportCapability

    Public ReadOnly Property IsSupported As Boolean Implements ITrxReportCapability.IsSupported
        Get
            Return True
        End Get
    End Property

    Public Property IsEnabled As Boolean
        Get
            Return _isEnabled
        End Get
        Private Set(value As Boolean)
            _isEnabled = value
        End Set
    End Property

    Private _isEnabled As Boolean

    Public Sub Enable() Implements ITrxReportCapability.Enable
        IsEnabled = True
    End Sub
End Class

Zwróć zdolność z elementu capabilitiesFactory, który przekazujesz do RegisterTestFramework. Nadaj strukturze tę samą instancję możliwości, aby mogła obserwować Enable(). W przypadku ręcznie skomponowanej aplikacji testowej zarejestruj dostawcę TRX po frameworku i innych rozszerzeniach:

var trxCapability = new TrxCapability();
var builder = await TestApplication.CreateBuilderAsync(args);
builder.RegisterTestFramework(
    _ => new TestFrameworkCapabilities(trxCapability),
    (_, _) => new ReportFramework(trxCapability));
builder.AddTrxReportProvider();
Dim trxCapability As New TrxCapability()
Dim builder = Await TestApplication.CreateBuilderAsync(args)
builder.RegisterTestFramework(
    Function(services) New TestFrameworkCapabilities(trxCapability),
    Function(capabilities, services) New ReportFramework(trxCapability))
builder.AddTrxReportProvider()

Pakiet abstrakcji zawiera właściwości typizowane dla TestNode.Properties:

Property Purpose
TrxFullyQualifiedTypeNameProperty Dostarcza w pełni kwalifikowaną nazwę klasy testowej.
TrxCategoriesProperty Dostarcza kategorie testu.
TrxExceptionProperty Dostarcza komunikat o niepowodzeniu i ślad stosu.
TrxMessagesProperty Zawiera StandardOutputTrxMessagewpisy, StandardErrorTrxMessagei DebugOrTraceTrxMessage wpisy.
TrxTestDefinitionName Zastępuje nazwę definicji testu w raporcie.
TrxWorkItemsProperty Dostarcza identyfikatory elementów roboczych.

Na przykład wzbogać wynik po określeniu wyniku testu przez framework:

if (trxCapability.IsEnabled)
{
    testNode.Properties.Add(
        new TrxFullyQualifiedTypeNameProperty("Contoso.CalculatorTests"));
    testNode.Properties.Add(new TrxCategoriesProperty(["Unit"]));
    testNode.Properties.Add(new TrxMessagesProperty(
        [new StandardOutputTrxMessage($"Captured output for {test.Name}.")]));
    if (failure is not null)
    {
        testNode.Properties.Add(
            new TrxExceptionProperty(failure.Message, failure.StackTrace));
    }
}
If _trxCapability.IsEnabled Then
    testNode.Properties.Add(
        New TrxFullyQualifiedTypeNameProperty("Contoso.CalculatorTests"))
    testNode.Properties.Add(New TrxCategoriesProperty({"Unit"}))
    testNode.Properties.Add(New TrxMessagesProperty(
        {New StandardOutputTrxMessage($"Captured output for {test.Name}.")}))
    If failure IsNot Nothing Then
        testNode.Properties.Add(
            New TrxExceptionProperty(failure.Message, failure.StackTrace))
    End If
End If

Te właściwości uzupełniają wymagany stan węzła testowego; nie zastępują go. Komunikaty specyficzne dla protokołu TRX również nie zastępują elementów platformy StandardOutputProperty i StandardErrorProperty.

Kompletna próbka zawiera jeden pomyślny test i jeden celowo zakończony niepowodzeniem test. W programie --report-trx zapisuje zarówno wyniki, jak i wzbogacone metadane do pliku TRX, a następnie zwraca kod zakończenia 2. Aby uruchomić tylko test zakończony powodzeniem, dodaj polecenie --filter-uid trx.pass.

Jeśli funkcja jest nieobecna lub IsSupported jest false, reporter TRX ostrzega, że framework nie obsługuje wzbogacania TRX. Reporter nadal może utworzyć raport na podstawie dostępnych danych, ale może brakować szczegółów specyficznych dla frameworka. Nie ogłaszaj obsługi, chyba że framework dostarcza odpowiednie metadane.

IGracefulStopTestExecutionCapability

Aby obsługiwać --maximum-failed-tests w niestandardowym frameworku testowym, zaimplementuj Microsoft.Testing.Platform.Capabilities.TestFramework.IGracefulStopTestExecutionCapability. Funkcja jest częścią pakietu Microsoft.Testing.Platform i jest dostępna od wersji MTP 1.5. Autorzy testów korzystający z frameworka, który już obsługuje tę opcję, nie muszą implementować funkcjonalności.

Interfejs definiuje Task StopTestExecutionAsync(CancellationToken cancellationToken). Gdy liczba błędów osiągnie żądaną wartość maksymalną, MTP wywołuje tę metodę. Twoja implementacja musi poinformować framework, aby przestał planować nowe testy, jednocześnie pozwalając testom, które już się rozpoczęły, zakończyć się, przeprowadzić czyszczenie i opublikować ich wyniki.

Important

Możliwość i AddMaximumFailedTestsService pomocnik to eksperymentalne interfejsy API, które generują diagnostykę kompilatora TPEXP. Ich kontrakty mogą ulec zmianie lub zostać usunięte. Przykładowe projekty jawnie wyłączają TPEXP.

Podczas implementowania funkcji należy spełnić następujące wymagania:

  • Udostępnianie stanu zatrzymania między funkcją a harmonogramem testów frameworka. Jedynie zwracanie ukończonego zadania nie zatrzymuje testów.
  • Spraw, aby żądania zatrzymania były bezpieczne dla wątków i idempotentne. Więcej niż jeden składnik platformy może zażądać zatrzymania.
  • Wróć po zasygnalizowaniu zatrzymania. Nie czekaj na zakończenie całego przebiegu ani na zakończenie działania magistrali komunikatów: MTP może wywołać metodę podczas przetwarzania wyniku testu, dzięki czemu oczekiwanie może zakleszczyć.
  • Zachowaj stan graceful-stop oddzielony od anulowania żądania. Nie przerywaj procesu ani nie anuluj publikacji wyników w celu zaimplementowania łagodnego zatrzymania. Uwzględnij token anulowania metody, jeśli operacja zatrzymania wymaga pracy asynchronicznej.
  • Po zakończeniu aktywnych testów ukończ normalny cykl życia żądania, w tym publikację wyników, oczyszczanie i wywołanie metody ExecuteRequestContext.Complete().

Poniższa implementacja używa flagi bezpiecznej wątkowo. W ramach frameworku sprawdź IsStopRequested przed zaplanowaniem każdego testu i przestań podejmować nową pracę, gdy flaga to true.

public sealed class GracefulStopCapability : IGracefulStopTestExecutionCapability
{
    private int _stopRequested;

    public bool IsStopRequested => Volatile.Read(ref _stopRequested) != 0;

    public Task StopTestExecutionAsync(CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        Interlocked.Exchange(ref _stopRequested, 1);
        return Task.CompletedTask;
    }
}
Public NotInheritable Class GracefulStopCapability
    Implements IGracefulStopTestExecutionCapability

    Private _stopRequested As Integer

    Public ReadOnly Property IsStopRequested As Boolean
        Get
            Return Volatile.Read(_stopRequested) <> 0
        End Get
    End Property

    Public Function StopTestExecutionAsync(cancellationToken As CancellationToken) As Task _
        Implements IGracefulStopTestExecutionCapability.StopTestExecutionAsync

        cancellationToken.ThrowIfCancellationRequested()
        Interlocked.Exchange(_stopRequested, 1)
        Return Task.CompletedTask
    End Function
End Class

Udostępnij tę możliwość przez ITestFrameworkCapabilities.Capabilities. W przykładzie rejestracji użyto kolekcji TestFrameworkCapabilities MTP.

Podczas rejestracji frameworka testowego przekaż zarejestrowaną instancję capability do swojego frameworka. Poniższy pomocnik akceptuje fabrykę frameworka i obiekt IExtension, który jest właścicielem opcji wiersza polecenia. Wywołuje również AddMaximumFailedTestsService z przestrzeni nazw Microsoft.Testing.Platform.Helpers, aby zarejestrować dostawcę opcji --maximum-failed-tests. Bez tego dostawcy usługa MTP nie rozpoznaje tej opcji.

public static void Register(
    ITestApplicationBuilder builder,
    IExtension owner,
    Func<GracefulStopCapability, ITestFramework> createFramework)
{
    builder.RegisterTestFramework(
        _ => new TestFrameworkCapabilities(new GracefulStopCapability()),
        (capabilities, _) => createFramework(
            capabilities.Capabilities.OfType<GracefulStopCapability>().Single()));
    builder.AddMaximumFailedTestsService(owner);
}
Public Sub Register(
    builder As ITestApplicationBuilder,
    owner As IExtension,
    createFramework As Func(Of GracefulStopCapability, ITestFramework))

    builder.RegisterTestFramework(
        Function(services) New TestFrameworkCapabilities(New GracefulStopCapability()),
        Function(capabilities, services) createFramework(
            capabilities.Capabilities.OfType(Of GracefulStopCapability)().Single()))
    builder.AddMaximumFailedTestsService(owner)
End Sub

Fabryka capability tworzy nowy stan zatrzymania, a fabryka frameworka pobiera to samo wystąpienie. Nie twórz drugiego, rozłączonego mechanizmu w twojej strukturze. W przypadku frameworków obsługujących wiele żądań uruchamiania należy zachować stan zatrzymania ograniczony do odpowiedniego wykonania. Nie usuwaj zaakceptowanego żądania zatrzymania podczas uruchamiania wykonywania.

Funkcja MTP odrzuca --maximum-failed-tests podczas walidacji wiersza polecenia, jeśli dostawca opcji jest zarejestrowany, ale struktura nie uwidacznia tej możliwości. Gdy próg niepowodzenia wyzwala zatrzymanie, funkcja MTP zwraca kod 13zakończenia . Końcowa liczba niepowodzeń może przekroczyć próg, ponieważ przetwarzanie wyników jest asynchroniczne, a testy mogą już być w toku.

Informacje o kontrakcie interfejsu znajdziesz w IGracefulStopTestExecutionCapability źródle.