Udostępnij za pośrednictwem


Tworzenie struktury testowej

W tym artykule wyjaśniono, jak utworzyć niestandardową strukturę testową dla platformy Microsoft.Testing.Platform. Platforma testowa jest jedynym obowiązkowym rozszerzeniem. Odnajduje i uruchamia testy oraz raportuje wyniki z powrotem do platformy.

Aby zapoznać się z pełnym podsumowaniem punktu rozszerzenia oraz koncepcjami in-process/out-of-process, odwiedź Tworzenie rozszerzeń niestandardowych.

Jeśli migrujesz istniejącą strukturę testową opartą na programie VSTest, implementacja interfejsu ITestFramework natywnie jest zalecaną metodą. Rozszerzenie VSTest Bridge jest dostępne jako krok przejściowy, ale implementacja natywna zapewnia najlepsze środowisko.

Rozszerzenie platformy testowej

Platforma testowa to podstawowe rozszerzenie, które zapewnia platformie testowej możliwość odnajdywania i wykonywania testów. Framework testowy jest odpowiedzialny za przekazywanie wyników testów do platformy testowej. Platforma testowa jest jedynym obowiązkowym rozszerzeniem wymaganym do wykonania sesji testowania.

Rejestrowanie struktury testowania

W tej sekcji wyjaśniono, jak zarejestrować platformę testową na platformie testowania. Rejestrujesz tylko jedną strukturę testowania dla konstruktora aplikacji testowych przy użyciu interfejsu TestApplication.RegisterTestFramework API, jak pokazano w dokumentacji architektury Microsoft.Testing.Platform .

Interfejs API rejestracji jest definiowany w następujący sposób:

ITestApplicationBuilder RegisterTestFramework(
    Func<IServiceProvider, ITestFrameworkCapabilities> capabilitiesFactory,
    Func<ITestFrameworkCapabilities, IServiceProvider, ITestFramework> adapterFactory);

Interfejs API RegisterTestFramework wymaga dwóch fabryk:

  1. Func<IServiceProvider, ITestFrameworkCapabilities>: jest to delegat, który akceptuje obiekt implementujący interfejs IServiceProvider i zwraca obiekt implementujący interfejs ITestFrameworkCapabilities. IServiceProvider zapewnia dostęp do usług platformy, takich jak konfiguracje, rejestratory i argumenty wiersza polecenia.

    Interfejs ITestFrameworkCapabilities służy do ogłaszania możliwości obsługiwanych przez framework testowy do platformy i rozszerzeń. Dzięki niej platforma i rozszerzenia mogą prawidłowo współdziałać, implementując i obsługując określone zachowania. Aby lepiej zrozumieć koncepcję możliwości, zapoznaj się z odpowiednim rozdziałem.

  2. Func<ITestFrameworkCapabilities, IServiceProvider, ITestFramework>: jest to delegat, który przyjmuje obiekt ITestFrameworkCapabilities, który jest wystąpieniem zwracanym przez Func<IServiceProvider, ITestFrameworkCapabilities>, oraz IServiceProvider w celu umożliwienia dostępu do usług platformy ponownie. Oczekiwanym obiektem zwracanym jest taki, który implementuje interfejs ITestFramework. ITestFramework służy jako silnik wykonawczy, który wykrywa i uruchamia testy, a następnie przekazuje wyniki z powrotem do platformy testowej.

Potrzeba oddzielenia tworzenia ITestFrameworkCapabilities i tworzenia ITestFramework jest optymalizacją, aby uniknąć tworzenia platformy testowej, jeśli obsługiwane możliwości nie są wystarczające do przeprowadzenia aktualnej sesji testowej.

Rozważmy następujący przykład kodu użytkownika, który demonstruje rejestrację platformy testowej, która zwraca pusty zestaw możliwości:

internal class TestingFrameworkCapabilities : ITestFrameworkCapabilities
{
    public IReadOnlyCollection<ITestFrameworkCapability> Capabilities => [];
}

internal class TestingFramework : ITestFramework
{
   public TestingFramework(ITestFrameworkCapabilities capabilities, IServiceProvider serviceProvider)
   {
       // ...
   }
   // Omitted for brevity...
}

public static class TestingFrameworkExtensions
{
    public static void AddTestingFramework(this ITestApplicationBuilder builder)
    {
        builder.RegisterTestFramework(
            _ => new TestingFrameworkCapabilities(),
            (capabilities, serviceProvider) => new TestingFramework(capabilities, serviceProvider));
    }
}

// ...

Teraz rozważ odpowiedni punkt wejścia tego przykładu przy użyciu kodu rejestracji:

var testApplicationBuilder = await TestApplication.CreateBuilderAsync(args);
// Register the testing framework
testApplicationBuilder.AddTestingFramework();
using var testApplication = await testApplicationBuilder.BuildAsync();
return await testApplication.RunAsync();

Uwaga / Notatka

Zwracanie pustych ITestFrameworkCapabilities nie powinno uniemożliwiać przeprowadzania sesji testowej. Wszystkie struktury testowe powinny być w stanie odnajdywać i uruchamiać testy. Wpływ powinien być ograniczony do rozszerzeń, które mogą zrezygnować, jeśli struktura testowa nie ma określonej funkcji.

Tworzenie platformy testowania

Microsoft.Testing.Platform.Extensions.TestFramework.ITestFramework jest implementowany przez rozszerzenia, które zapewniają platformę testową:

public interface ITestFramework : IExtension
{
    Task<CreateTestSessionResult> CreateTestSessionAsync(CreateTestSessionContext context);
    Task ExecuteRequestAsync(ExecuteRequestContext context);
    Task<CloseTestSessionResult> CloseTestSessionAsync(CloseTestSessionContext context);
}

Interfejs IExtension

Interfejs ITestFramework dziedziczy z interfejsu IExtension, który jest interfejsem, od którego dziedziczą wszystkie punkty rozszerzenia. IExtension służy do pobierania nazwy i opisu rozszerzenia. IExtension zapewnia również sposób dynamicznego włączania lub wyłączania rozszerzenia w konfiguracji za pośrednictwem Task<bool> IsEnabledAsync(). Upewnij się, że wrócisz true z tej metody, jeśli nie masz określonych wymagań, aby ją wyłączyć.

Metoda CreateTestSessionAsync

Metoda CreateTestSessionAsync jest wywoływana na początku sesji testowej i służy do inicjowania struktury testowej. Interfejs API akceptuje obiekt CreateTestSessionContext i zwraca CreateTestSessionResult.

public sealed class CreateTestSessionContext : TestSessionContext
{
    public CancellationToken CancellationToken { get; }
}

Właściwość SessionUid jest dziedziczona po TestSessionContext (zobacz sekcję TestSessionContext). CancellationToken służy do zatrzymania wykonywania CreateTestSessionAsync.

Obiekt zwracany jest CreateTestSessionResult:

public sealed class CreateTestSessionResult
{
    public string? WarningMessage { get; set; }
    public string? ErrorMessage { get; set; }
    public bool IsSuccess { get; set; }
}

Właściwość IsSuccess służy do wskazywania, czy tworzenie sesji zakończyło się pomyślnie. Gdy zwraca false, wykonywanie testu zostanie zatrzymane.

Metoda CloseTestSessionAsync

Metoda CloseTestSessionAsync jest zestawiona z CreateTestSessionAsync w funkcjonalności, a jedyną różnicą jest nazwa obiektów. Aby uzyskać więcej informacji, zobacz sekcję CreateTestSessionAsync.

Metoda ExecuteRequestAsync

Metoda ExecuteRequestAsync akceptuje obiekt typu ExecuteRequestContext. Ten obiekt, zgodnie z sugestią jego nazwy, zawiera szczegółowe informacje o akcji, którą ma wykonać struktura testowa. Definicja ExecuteRequestContext to:

public sealed class ExecuteRequestContext
{
    public IRequest Request { get; }
    public IMessageBus MessageBus { get; }
    public CancellationToken CancellationToken { get; }
    public void Complete();
}

IRequest: jest to podstawowy interfejs dla dowolnego typu żądania. Należy traktować strukturę testową jako serwer stanowy w procesie, w którym jest cykl życia:

Diagram sekwencji reprezentujący cykl życia platformy testowej.

Na powyższym diagramie pokazano, że platforma testowania wysyła trzy żądania po utworzeniu wystąpienia platformy testowej. Platforma testowa przetwarza te żądania i korzysta z usługi IMessageBus, która jest zawarta w samym żądaniu, aby dostarczyć wynik dla każdego konkretnego żądania. Po obsłużeniu określonego żądania platforma testowa wywołuje na nim metodę Complete() wskazującą na platformę testowania, że żądanie zostało spełnione. Platforma testowa monitoruje wszystkie wysłane żądania. Po spełnieniu wszystkich żądań wywołuje CloseTestSessionAsync i usuwa wystąpienie (jeśli IDisposable/IAsyncDisposable jest implementowany). Widać, że żądania i ich uzupełnienia mogą się nakładać, umożliwiając współbieżne i asynchroniczne wykonywanie żądań.

Uwaga / Notatka

Obecnie platforma testowa nie wysyła nakładających się żądań i czeka na ukończenie żądania >> przed wysłaniem następnego. Jednak to zachowanie może ulec zmianie w przyszłości. Obsługa żądań współbieżnych będzie określana poprzez system możliwości.

Implementacja IRequest określa dokładne żądanie, które należy spełnić. Platforma testowa identyfikuje typ żądania i odpowiednio je obsługuje. Jeśli typ żądania jest nierozpoznany, należy zgłosić wyjątek.

Szczegółowe informacje o dostępnych żądaniach można znaleźć w sekcji IRequest.

IMessageBus: ta usługa połączona z żądaniem umożliwia platformie testowej asynchronicznie w celu opublikowania informacji o trwającym żądaniu na platformie testowej. Magistrala komunikatów służy jako centralny koncentrator dla platformy, ułatwiając asynchroniczną komunikację między wszystkimi składnikami i rozszerzeniami platformy. Aby uzyskać kompleksową listę informacji, które można opublikować na platformie testowania, zapoznaj się z sekcją IMessageBus.

CancellationToken: ten token jest używany do przerywania przetwarzania określonego żądania.

Complete(): Jak pokazano w poprzedniej sekwencji, metoda Complete powiadamia platformę, że żądanie zostało pomyślnie przetworzone, a wszystkie istotne informacje zostały przesłane do IMessageBus.

Ostrzeżenie

Zaniedbanie wywołania Complete() na żądaniu spowoduje, że aplikacja testowa stanie się nieodpowiadająca.

Aby dostosować strukturę testową zgodnie z twoimi wymaganiami lub wymaganiami użytkowników, możesz użyć spersonalizowanej sekcji w pliku konfiguracji lub niestandardowych opcji wiersza polecenia .

Obsługa żądań

W kolejnej sekcji przedstawiono szczegółowy opis różnych żądań, które mogą odbierać i przetwarzać struktury testowe.

Przed przejściem do następnej sekcji należy dokładnie zrozumieć koncepcję IMessageBus, która jest podstawową usługą do przekazywania informacji o wykonywaniu testów do platformy testowania.

TestSessionContext

TestSessionContext jest właściwością udostępnioną we wszystkich żądaniach, dostarczając informacje o trwającej sesji testowej:

public class TestSessionContext
{
    public SessionUid SessionUid { get; }
}

public readonly struct SessionUid(string value)
{
    public string Value { get; }
}

TestSessionContext składa się z SessionUid, unikatowego identyfikatora trwającej sesji testowej, który ułatwia rejestrowanie i korelowanie danych sesji testowej.

DiscoverTestExecutionRequest

public class DiscoverTestExecutionRequest
{
    public TestSessionContext Session { get; }
    public ITestExecutionFilter Filter { get; }
}

DiscoverTestExecutionRequest nakazuje środowisku testowemu , aby odkrywało testy i przekazywało te informacje do IMessageBus.

Jak opisano w poprzedniej sekcji, właściwość odnalezionego testu jest DiscoveredTestNodeStateProperty. Oto ogólny fragment kodu do celów referencyjnych:

var testNode = new TestNode
{
    Uid = GenerateUniqueStableId(),
    DisplayName = GetDisplayName(),
    Properties = new PropertyBag(
        DiscoveredTestNodeStateProperty.CachedInstance),
};

await context.MessageBus.PublishAsync(
    this,
    new TestNodeUpdateMessage(
        discoverTestExecutionRequest.Session.SessionUid,
        testNode));

// ...

RunTestExecutionRequest

public class RunTestExecutionRequest
{
    public TestSessionContext Session { get; }
    public ITestExecutionFilter Filter { get; }
}

RunTestExecutionRequest instruuje platformę testową , aby wykonała testy i przekazała te informacje do IMessageBus.

Oto ogólny fragment kodu do celów referencyjnych:

var skippedTestNode = new TestNode()
{
    Uid = GenerateUniqueStableId(),
    DisplayName = GetDisplayName(),
    Properties = new PropertyBag(
        SkippedTestNodeStateProperty.CachedInstance),
};

await context.MessageBus.PublishAsync(
    this,
    new TestNodeUpdateMessage(
        runTestExecutionRequest.Session.SessionUid,
        skippedTestNode));

// ...

var successfulTestNode = new TestNode()
{
    Uid = GenerateUniqueStableId(),
    DisplayName = GetDisplayName(),
    Properties = new PropertyBag(
        PassedTestNodeStateProperty.CachedInstance),
};

await context.MessageBus.PublishAsync(
    this,
    new TestNodeUpdateMessage(
        runTestExecutionRequest.Session.SessionUid,
        successfulTestNode));

// ...

var assertionFailedTestNode = new TestNode()
{
    Uid = GenerateUniqueStableId(),
    DisplayName = GetDisplayName(),
    Properties = new PropertyBag(
        new FailedTestNodeStateProperty(assertionException)),
};

await context.MessageBus.PublishAsync(
    this,
    new TestNodeUpdateMessage(
        runTestExecutionRequest.Session.SessionUid,
        assertionFailedTestNode));

// ...

var failedTestNode = new TestNode()
{
    Uid = GenerateUniqueStableId(),
    DisplayName = GetDisplayName(),
    Properties = new PropertyBag(
        new ErrorTestNodeStateProperty(ex.InnerException!)),
};

await context.MessageBus.PublishAsync(
    this,
    new TestNodeUpdateMessage(
        runTestExecutionRequest.Session.SessionUid,
        failedTestNode));

Dane TestNodeUpdateMessage

Jak wspomniano w sekcji IMessageBus, przed użyciem magistrali komunikatów należy określić typ danych, które mają zostać podane. Platforma testowania zdefiniowała dobrze znany typ, TestNodeUpdateMessage, aby reprezentować koncepcję informacji o aktualizacji testów .

W tej części dokumentu wyjaśniono, jak korzystać z tych danych ładunku. Przyjrzyjmy się powierzchni:

public sealed class TestNodeUpdateMessage(
    SessionUid sessionUid,
    TestNode testNode,
    TestNodeUid? parentTestNodeUid = null)
{
    public TestNode TestNode { get; }
    public TestNodeUid? ParentTestNodeUid { get; }
}

public class TestNode
{
    public required TestNodeUid Uid { get; init; }
    public required string DisplayName { get; init; }
    public PropertyBag Properties { get; init; } = new();
}

public sealed class TestNodeUid(string value);

public sealed partial class PropertyBag
{
    public PropertyBag();
    public PropertyBag(params IProperty[] properties);
    public PropertyBag(IEnumerable<IProperty> properties);
    public int Count { get; }
    public void Add(IProperty property);
    public bool Any<TProperty>();
    public TProperty? SingleOrDefault<TProperty>();
    public TProperty Single<TProperty>();
    public TProperty[] OfType<TProperty>();
    public IEnumerable<IProperty> AsEnumerable();
    public IEnumerator<IProperty> GetEnumerator();
    ...
}

public interface IProperty
{
}
  • TestNodeUpdateMessage: TestNodeUpdateMessage składa się z dwóch właściwości: TestNode i ParentTestNodeUid. ParentTestNodeUid wskazuje, że test może mieć test nadrzędny, wprowadzając koncepcję drzewa testowego , w którym TestNodemożna ustawić względem siebie. Ta struktura umożliwia przyszłe ulepszenia oraz cechy użytkowe w oparciu o relację drzewa między węzłami. Jeśli struktura testowa nie wymaga struktury drzewa testowego, możesz zrezygnować z jej używania i po prostu ustawić ją na wartość null, co spowoduje prostą płaską listę TestNodes.

  • TestNode: TestNode składa się z trzech właściwości, z których jedna jest Uid typu TestNodeUid. Ta Uid służy jako STABILNY unikatowy identyfikator dla węzła. Termin UNIKATOWY STABILNY IDENTYFIKATOR oznacza, że ta sama TestNode powinna zachowywać IDENTYCZNEUid w różnych uruchomieniach i systemach operacyjnych. TestNodeUid jest dowolnym nieprzezroczystym ciągiem, które platforma testowa akceptuje tak.

Ważna

Stabilność i unikatowość identyfikatora mają kluczowe znaczenie w domenie testowej. Umożliwiają one precyzyjne ukierunkowanie pojedynczego testu do wykonania i umożliwiają wykorzystanie identyfikatora jako stałego identyfikatora testu, ułatwiając zaawansowane rozszerzenia i funkcjonalności.

Druga właściwość to DisplayName, która jest przyjazną dla człowieka nazwą testu. Na przykład ta nazwa jest wyświetlana podczas wykonywania --list-tests wiersza polecenia.

Trzeci atrybut to Properties, który jest typem PropertyBag. Jak pokazano w kodzie, jest to wyspecjalizowany zbiór właściwości, który zawiera właściwości ogólne dotyczące TestNodeUpdateMessage. Oznacza to, że można dołączyć dowolną właściwość do węzła, który implementuje interfejs zastępczy IProperty.

Platforma testowania identyfikuje określone właściwości dodane do TestNode.Properties w celu określenia, czy test zakończył się pomyślnie, nie powiodło się, czy został pominięty.

Można znaleźć bieżącą listę dostępnych właściwości wraz z opisem kontekstowym w sekcji TestNodeUpdateMessage.TestNode.

Typ PropertyBag jest zazwyczaj dostępny w każdym IData i jest używany do przechowywania różnych właściwości, które mogą być zapytane przez platformę i rozszerzenia. Ten mechanizm pozwala nam ulepszać platformę nowymi informacjami bez wprowadzania zmian powodujących problemy z kompatybilnością. Jeśli składnik rozpoznaje właściwość, może wysłać do niego zapytanie; w przeciwnym razie zlekceważy go.

Na koniec ta sekcja wyjaśnia, że implementacja frameworku testowego wymaga zaimplementowania IDataProducer, która generuje TestNodeUpdateMessage, jak w poniższym przykładzie:

internal sealed class TestingFramework
    : ITestFramework, IDataProducer
{
   // ...

   public Type[] DataTypesProduced =>
   [
       typeof(TestNodeUpdateMessage)
   ];

   // ...
}

Jeśli adapter testowy wymaga publikacji plików podczas wykonywania testów, można znaleźć rozpoznane właściwości w tym pliku źródłowym: https://github.com/microsoft/testfx/blob/main/src/Platform/Microsoft.Testing.Platform/Messages/FileArtifacts.cs. Jak widać, możesz ogólnie udostępniać zasoby plików lub skojarzyć je z określonym TestNode. Pamiętaj, że jeśli zamierzasz przesłać SessionFileArtifact, musisz zgłosić to na platformie z wyprzedzeniem, zgodnie z poniższymi wskazówkami:

internal sealed class TestingFramework
    : ITestFramework, IDataProducer
{
   // ...

   public Type[] DataTypesProduced =>
   [
       typeof(TestNodeUpdateMessage),
       typeof(SessionFileArtifact)
   ];

   // ...
}

Dobrze znane właściwości

Jak opisano w sekcji żądania, platforma testowania identyfikuje określone właściwości dodane do TestNodeUpdateMessage, aby określić stan TestNode (na przykład sukces, niepowodzenie, pominięcie itp.). Dzięki temu środowisko uruchomieniowe może dokładnie wyświetlić listę testów, które zakończyły się niepowodzeniem, z odpowiednimi informacjami w konsoli programu oraz ustawić odpowiedni kod zakończenia dla procesu testowania.

W tym segmencie wyjaśnimy różne dobrze znane opcje IProperty i ich odpowiednie implikacje.

Aby uzyskać kompleksową listę dobrze znanych właściwości, zobacz TestNodeProperties.cs. Jeśli zauważysz, że brakuje opisu właściwości, zgłoś problem.

Te właściwości można podzielić w następujących kategoriach:

  1. ogólne informacje: właściwości, które można uwzględnić w dowolnym rodzaju żądania.
  2. informacje dotyczące wykrywania: właściwości dostarczane podczas żądania wykrywania DiscoverTestExecutionRequest.
  3. informacje o wykonaniu: właściwości dostarczane podczas żądania wykonania testu RunTestExecutionRequest.

Niektóre właściwości są wymagane, a inne są opcjonalne. Wymagane są obowiązkowe właściwości, aby zapewnić podstawowe funkcje testowania, takie jak raportowanie testów zakończonych niepowodzeniem i wskazanie, czy cała sesja testowa zakończyła się pomyślnie, czy nie.

Z drugiej strony opcjonalne właściwości rozszerzają środowisko testowania, udostępniając dodatkowe informacje. Są one szczególnie przydatne w scenariuszach IDE (takich jak VS, VSCode itp.), w uruchomieniach konsoli lub podczas obsługi określonych rozszerzeń, które wymagają bardziej szczegółowych informacji, aby poprawnie działać. Jednak te opcjonalne właściwości nie mają wpływu na wykonywanie testów.

Uwaga / Notatka

Rozszerzenia mają za zadanie zgłaszanie alertów i zarządzanie wyjątkami, gdy wymagają one poprawnego działania określonych informacji. Jeśli rozszerzenie nie ma niezbędnych informacji, nie powinno spowodować niepowodzenia wykonywania testu, ale raczej powinno po prostu zrezygnować z wykonania testów.

Informacje ogólne
public record KeyValuePairStringProperty(
    string Key,
    string Value)
        : IProperty;

KeyValuePairStringProperty oznacza ogólne dane pary klucz/wartość.

public record struct LinePosition(
    int Line,
    int Column);

public record struct LinePositionSpan(
    LinePosition Start,
    LinePosition End);

public abstract record FileLocationProperty(
    string FilePath,
    LinePositionSpan LineSpan)
        : IProperty;

public sealed record TestFileLocationProperty(
    string FilePath,
    LinePositionSpan LineSpan)
        : FileLocationProperty(FilePath, LineSpan);

TestFileLocationProperty służy do określania lokalizacji testu w pliku źródłowym. Jest to szczególnie przydatne, gdy inicjator jest środowiskiem IDE, takim jak Visual Studio lub Visual Studio Code.

public sealed record TestMethodIdentifierProperty(
    string AssemblyFullName,
    string Namespace,
    string TypeName,
    string MethodName,
    string[] ParameterTypeFullNames,
    string ReturnTypeFullName)

TestMethodIdentifierProperty jest unikatowym identyfikatorem metody testowej.

public sealed record TestMetadataProperty(
    string Key,
    string Value)

TestMetadataProperty służy do przekazywania charakterystyk lub cechTestNode.

Informacje o odnajdowaniu
public sealed record DiscoveredTestNodeStateProperty(
    string? Explanation = null)
{
    public static DiscoveredTestNodeStateProperty CachedInstance { get; }
}

DiscoveredTestNodeStateProperty wskazuje, że ten TestNode został odnaleziony. Jest on używany, gdy DiscoverTestExecutionRequest jest wysyłany do platformy testowej. Zwróć uwagę na przydatną wartość buforowaną dostępną przez właściwość CachedInstance. Ta właściwość jest wymagana.

Informacje o wykonaniu
public sealed record InProgressTestNodeStateProperty(
    string? Explanation = null)
{
    public static InProgressTestNodeStateProperty CachedInstance { get; }
}

InProgressTestNodeStateProperty informuje platformę testową, że wykonanie TestNode zostało zaplanowane i jest obecnie w toku. Zwróć uwagę na przydatną wartość buforowaną dostępną przez właściwość CachedInstance.

public readonly record struct TimingInfo(
    DateTimeOffset StartTime,
    DateTimeOffset EndTime,
    TimeSpan Duration);

public sealed record StepTimingInfo(
    string Id,
    string Description,
    TimingInfo Timing);

public sealed record TimingProperty : IProperty
{
    public TimingProperty(TimingInfo globalTiming)
        : this(globalTiming, [])
    {
    }

    public TimingProperty(
        TimingInfo globalTiming,
        StepTimingInfo[] stepTimings)
    {
        GlobalTiming = globalTiming;
        StepTimings = stepTimings;
    }

    public TimingInfo GlobalTiming { get; }

    public StepTimingInfo[] StepTimings { get; }
}

TimingProperty jest używany do przekazywania szczegółów czasu dotyczących wykonywania TestNode. Umożliwia również chronometraż poszczególnych kroków wykonywania za pośrednictwem StepTimingInfo. Jest to szczególnie przydatne, gdy koncepcja testu jest podzielona na wiele faz, takich jak inicjowanie, wykonywanie i oczyszczanie.

jedna i tylko jedna następująca właściwość jest wymagana na każdy TestNode i komunikuje wynik do TestNode platformy testowej.

public sealed record PassedTestNodeStateProperty(
    string? Explanation = null)
        : TestNodeStateProperty(Explanation)
{
    public static PassedTestNodeStateProperty CachedInstance
        { get; } = new PassedTestNodeStateProperty();
}

PassedTestNodeStateProperty informuje platformę testowania, że ten TestNode jest zdany. Zwróć uwagę na przydatną wartość buforowaną dostępną przez właściwość CachedInstance.

public sealed record SkippedTestNodeStateProperty(
    string? Explanation = null)
        : TestNodeStateProperty(Explanation)
{
    public static SkippedTestNodeStateProperty CachedInstance
        { get; } =  new SkippedTestNodeStateProperty();
}

SkippedTestNodeStateProperty informuje platformę testową, że ta TestNode została pominięta. Zwróć uwagę na przydatną wartość buforowaną dostępną przez właściwość CachedInstance.

public sealed record FailedTestNodeStateProperty : TestNodeStateProperty
{
    public FailedTestNodeStateProperty()
        : base(default(string))
    {
    }

    public FailedTestNodeStateProperty(string explanation)
        : base(explanation)
    {
    }

    public FailedTestNodeStateProperty(
        Exception exception,
        string? explanation = null)
        : base(explanation ?? exception.Message)
    {
        Exception = exception;
    }

    public Exception? Exception { get; }
}

FailedTestNodeStateProperty informuje platformę testową, że ta TestNode nie powiodła się po asercji.

public sealed record ErrorTestNodeStateProperty : TestNodeStateProperty
{
    public ErrorTestNodeStateProperty()
        : base(default(string))
    {
    }

    public ErrorTestNodeStateProperty(string explanation)
        : base(explanation)
    {
    }

    public ErrorTestNodeStateProperty(
        Exception exception,
        string? explanation = null)
            : base(explanation ?? exception.Message)
    {
        Exception = exception;
    }

    public Exception? Exception { get; }
}

ErrorTestNodeStateProperty informuje platformę testowania, że ta TestNode nie powiodła się. Ten typ błędu różni się od FailedTestNodeStateProperty, który jest używany w przypadku błędów asercji. Możesz na przykład zgłaszać problemy, takie jak błędy inicjowania testów z ErrorTestNodeStateProperty.

public sealed record TimeoutTestNodeStateProperty : TestNodeStateProperty
{
    public TimeoutTestNodeStateProperty()
        : base(default(string))
    {
    }

    public TimeoutTestNodeStateProperty(string explanation)
        : base(explanation)
    {
    }

    public TimeoutTestNodeStateProperty(
        Exception exception,
        string? explanation = null)
            : base(explanation ?? exception.Message)
    {
        Exception = exception;
    }

    public Exception? Exception { get; }

    public TimeSpan? Timeout { get; init; }
}

TimeoutTestNodeStateProperty informuje platformę testową, że ta TestNode nie powiodła się z powodu timeoutu. Możesz zgłosić przekroczenie limitu czasu, używając właściwości Timeout.

public sealed record CancelledTestNodeStateProperty : TestNodeStateProperty
{
    public CancelledTestNodeStateProperty()
        : base(default(string))
    {
    }

    public CancelledTestNodeStateProperty(string explanation)
        : base(explanation)
    {
    }

    public CancelledTestNodeStateProperty(
        Exception exception,
        string? explanation = null)
        : base(explanation ?? exception.Message)
    {
        Exception = exception;
    }

    public Exception? Exception { get; }
}

CancelledTestNodeStateProperty informuje platformę testowania, że ta TestNode nie powiodła się z powodu anulowania.