Microsoft.Testing.Platform (MTP) fonctionnalités

Dans le contexte de MTP, une fonctionnalité fait référence au potentiel d’effectuer une action spécifique ou de fournir des informations spécifiques. Il s’agit d’un moyen pour l’infrastructure de test et les extensions de déclarer leur capacité à fonctionner d’une certaine manière ou à fournir des informations spécifiques aux demandeurs.

Les demandeurs peuvent être n’importe quel composant impliqué dans une session de test, comme la plateforme, une extension ou l’infrastructure de test elle-même.

L’objectif principal du système de capacité est de faciliter la communication efficace entre les composants impliqués dans une session de test, ce qui leur permet de exchange des informations et de répondre avec précision à leurs besoins respectifs.

Exemple guidé

Prenons un exemple hypothétique pour illustrer la nécessité d’un système de capacité.

Remarque

Cet exemple est purement à des fins d’illustration et n’est actuellement pas implémenté dans MTP ou tout framework de test.

Imaginez une situation où vous disposez d’une extension qui nécessite que l’infrastructure de test n’exécute pas plus d’un test à la fois. En outre, après chaque test, l’extension doit connaître l’utilisation du processeur pour ce test spécifique.

Pour prendre en charge le scénario précédent, vous devez vous renseigner auprès de l’infrastructure de test si :

  1. Il a la possibilité d’exécuter un seul test à la fois.
  2. Il peut fournir des informations sur la quantité d’UC consommée par chaque test.

Comment l’extension peut-elle déterminer si l’infrastructure de test peut fonctionner dans ce mode et fournir des informations d’utilisation du processeur pour une session de test ? Dans MTP, cette fonctionnalité est représentée par une implémentation de l’interface 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
{
}

Comme vous pouvez le voir, l’interface ICapability est une interface de marqueur , car elle peut représenter n’importe quelle fonctionnalité, et l’implémentation réelle dépend du contexte. Vous observerez également le ITestFrameworkCapability, qui hérite de ICapability pour classer la capacité. La nature générique du système de capacité permet un regroupement pratique par contexte. Le ITestFrameworkCapability groupe toutes les fonctionnalités implémentées par l’infrastructure de test. L’interface ICapabilities<TCapability> révèle l’ensemble de toutes les fonctionnalités implémentées par une extension. De même, pour la base, il existe un framework de test spécifique au contexte appelé ITestFrameworkCapabilities. Le ITestFrameworkCapabilities est fourni à la plateforme lors du processus d'enregistrement du framework de test.

Pour créer une fonctionnalité qui traite le scénario mentionné ci-dessus, vous la définissez comme suit :

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

Si l’infrastructure de test implémente cette interface, au moment de l’exécution, les requêtes suivantes peuvent être interrogées :

  • Vérifiez si l’infrastructure de test a la possibilité de désactiver le parallélisme CanDisableParallelism = true.
  • Déterminez si l’infrastructure de test peut fournir des données CanProvidePerTestCPUConsumption = trued’utilisation du processeur.
  • Demandez à l’adaptateur de test d’activer ce mode en appelant la Enable() méthode avant le début de la session de test.

Le fragment de code hypothétique à l’intérieur de l’extension peut ressembler à ceci :

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...
    }
}

L’exemple précédent montre comment l’infrastructure de capacité permet un mécanisme puissant pour communiquer les capacités entre les composants impliqués dans une session de test. Bien que l’exemple illustre une fonctionnalité spécifiquement conçue pour l’infrastructure de test, n’importe quel composant peut exposer et implémenter des extensions qui héritent de ICapability.

Il est évident que tous les détails ne peuvent pas être communiqués via une interface. Compte tenu de l’exemple précédent, que doit attendre l’extension si CanProvidePerTestCpuConsumption est pris en charge ? Quel type d’informations personnalisées est censé être transmis via iMessageBus par l’infrastructure de test ? La solution est la documentation de la fonctionnalité. Il incombe au propriétaire de la capacité de concevoir, d’expédier et de documenter le plus clairement possible pour aider les implémenteurs qui souhaitent collaborer efficacement avec l’extension qui nécessite la capacité spécifique.

Par exemple, l’extension de rapport TRX permet au framework de test d’implémenter la fonctionnalité nécessaire pour générer avec précision un rapport TRX. L'extension pour l'enregistrement est incluse dans le package https://www.nuget.org/packages/Microsoft.Testing.Extensions.TrxReport NuGet, mais la capacité de mise en œuvre se trouve dans le package contract only NuGet.

En conclusion, récapitulons les principaux aspects du système de capacité :

  • Il est essentiel de faciliter la communication claire et stable entre les composants.
  • Toutes les capacités doivent hériter de ICapability ou d'une interface qui hérite de celle-ci et sont exposées via une collection avec l'interface ICapabilities.
  • Il contribue à l'évolution des fonctionnalités sans provoquer de changements incompatibles. Si une certaine fonctionnalité n’est pas prise en charge, une action appropriée peut être effectuée.
  • La responsabilité de la conception, de l’expédition et de la documentation de l’utilisation d’une fonctionnalité réside avec le propriétaire de la fonctionnalité. MTP peut également posséder une fonctionnalité de la même façon que toute autre extension.

Capacités du cadre

La plateforme expose une interface spécialisée nommée ITestFrameworkCapability qui est la base de toutes les fonctionnalités exposées pour les frameworks de test. Ces fonctionnalités sont fournies lors de l’inscription de l’infrastructure de test à la plateforme.

IBannerMessageOwnerCapability

Une fonctionnalité facultative de l’infrastructure de test permet au framework de test de fournir le message de bannière à la plateforme. Si le message est null ou si la fonctionnalité n’est pas disponible, la plateforme utilise son message de bannière par défaut.

Cette implémentation de fonctionnalité vous permet d’extraire les différentes conditions que l’infrastructure de test peut avoir à prendre en compte lorsque vous décidez si le message de bannière doit être affiché ou non.

La plateforme expose le IPlatformInformation service pour fournir des informations sur la plateforme qui peuvent être utiles lors de la création de votre message de bannière personnalisé.

ITrxReportCapability

Pour fournir des métadonnées spécifiques au framework à l’extension de rapport TRX, implémentez Microsoft.Testing.Extensions.TrxReport.Abstractions.ITrxReportCapability. Référencez le package Microsoft.Testing.Extensions.TrxReport.Abstractions dans votre framework. L’application de test a également besoin du package Microsoft.Testing.Extensions.TrxReport pour créer des rapports.

La capacité sépare le support de l’activation :

  • Retournez true de IsSupported lorsque votre infrastructure peut fournir des métadonnées TRX.
  • Dans Enable(), enregistrez que le rapporteur TRX a demandé les métadonnées. Le reporter appelle cette méthode au début d’une session de test lorsque les rapports TRX sont activés.
  • Après l’activation, ajoutez les propriétés TRX aux nœuds de test que votre infrastructure publie.

La fonctionnalité suivante stocke l’état d’activation. Les exemples de projets ciblent .NET 10 et référencent 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

Retournez la fonctionnalité du capabilitiesFactory que vous passez à RegisterTestFramework. Donnez au framework la même instance de capacité afin qu’elle puisse observer Enable(). Pour une application de test composée manuellement, inscrivez le fournisseur TRX après votre framework et d’autres extensions :

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()

Le package d’abstraction fournit des propriétés typées pour TestNode.Properties:

Property Purpose
TrxFullyQualifiedTypeNameProperty Fournit le nom complet de la classe de test.
TrxCategoriesProperty Fournit les catégories du test.
TrxExceptionProperty Fournit le message d’échec et la trace de pile.
TrxMessagesProperty Contient les entrées StandardErrorTrxMessage, DebugOrTraceTrxMessage et StandardOutputTrxMessage.
TrxTestDefinitionName Remplace le nom de la définition de test dans le rapport.
TrxWorkItemsProperty Fournit des identificateurs d’élément de travail.

Par exemple, enrichissez un résultat une fois que votre framework a déterminé le résultat du test :

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

Ces propriétés complètent l’état de nœud de test requis ; elles ne le remplacent pas. Les messages spécifiques à TRX ne remplacent pas non plus les StandardOutputProperty et StandardErrorProperty de la plateforme.

L’exemple complet contient un test réussi et un test ayant échoué intentionnellement. Avec --report-trx, il écrit les résultats et les métadonnées enrichies dans un fichier TRX, puis retourne le code 2de sortie . Pour exécuter uniquement le test réussi, ajoutez --filter-uid trx.pass.

Si la fonctionnalité est absente ou IsSupported est false, le rapporteur TRX avertit que le framework ne prend pas en charge l’enrichissement TRX. Le générateur de rapports peut toujours créer un rapport à partir des données disponibles, mais des détails spécifiques au framework peuvent être manquants. N'indiquez pas la prise en charge, sauf si votre framework fournit les métadonnées correspondantes.

IGracefulStopTestExecutionCapability

Pour prendre en charge --maximum-failed-tests dans une infrastructure de test personnalisée, implémentez Microsoft.Testing.Platform.Capabilities.TestFramework.IGracefulStopTestExecutionCapability. La fonctionnalité fait partie du package Microsoft.Testing.Platform et est disponible à partir de MTP 1.5. Les auteurs de tests qui utilisent une infrastructure qui prend déjà en charge l’option n’ont pas besoin d’implémenter la capacité.

L’interface définit Task StopTestExecutionAsync(CancellationToken cancellationToken). Lorsque le nombre d’échecs atteint le maximum demandé, MTP appelle cette méthode. Votre implémentation doit indiquer à l’infrastructure d’arrêter la planification de nouveaux tests alors qu’elle permet aux tests qui ont déjà commencé à terminer, à exécuter le nettoyage et à publier leurs résultats.

Important

La fonctionnalité et le helper AddMaximumFailedTestsService sont des API expérimentales qui produisent des diagnostics TPEXP du compilateur. Leurs contrats peuvent changer ou être supprimés. Les projets d’exemple suppriment TPEXP explicitement.

Suivez ces exigences lorsque vous implémentez la fonctionnalité :

  • Partagez l’état d’arrêt entre la capacité et le planificateur de test du framework. Le simple retour d’une tâche terminée n’arrête pas les tests.
  • Rendre les demandes d’arrêt sûres pour les threads et idempotentes. Plusieurs composants de plateforme peuvent demander un arrêt.
  • Retournez après avoir signalé l’arrêt. N’attendez pas la fin de l’exécution entière ou du bus de messages : MTP peut appeler la méthode pendant qu’elle traite un résultat de test, ce qui peut provoquer un interblocage.
  • Conservez l’état d’arrêt progressif distinct de l’annulation de la requête. Ne terminez pas le processus ou n’annulez pas la publication des résultats pour implémenter un arrêt en douceur. Respectez le jeton d’annulation de la méthode si votre opération d’arrêt nécessite un travail asynchrone.
  • Une fois les tests actifs terminés, terminez le cycle de vie normal de la demande, y compris la publication des résultats, le nettoyage et l’appel à ExecuteRequestContext.Complete().

L’implémentation suivante utilise un indicateur sûr vis-à-vis des threads. Dans votre framework, vérifiez IsStopRequested avant de planifier chaque test et arrêtez de prendre de nouveaux travaux lorsque l’indicateur est 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

Exposer la fonctionnalité via ITestFrameworkCapabilities.Capabilities. L’exemple d’inscription utilise la collection de TestFrameworkCapabilities MTP.

Pendant l’enregistrement du framework de test, transmettez l’instance de capacité enregistrée à votre framework. L’assistant suivant accepte votre fabrique de framework et un IExtension qui possède l’option de ligne de commande. Il appelle AddMaximumFailedTestsService également à partir de l’espace Microsoft.Testing.Platform.Helpers de noms pour inscrire le fournisseur d’options --maximum-failed-tests . Sans ce fournisseur, MTP ne reconnaît pas l’option.

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

La fabrique de capacités crée un état d’arrêt frais et la fabrique de framework récupère cette même instance. Ne créez pas de seconde fonctionnalité déconnectée dans votre cadre. Pour les frameworks qui prennent en charge plusieurs requêtes d’exécution, conservez l’étendue de l’état d’arrêt à l’exécution correspondante. N’effacez pas une demande d’arrêt acceptée au démarrage de l’exécution.

MTP rejette --maximum-failed-tests pendant la validation de ligne de commande si le fournisseur d’options est inscrit, mais que l’infrastructure n’expose pas cette fonctionnalité. Lorsque le seuil d’échec déclenche un arrêt, MTP retourne le code 13de sortie . Le nombre final d’échecs peut dépasser le seuil, car le traitement des résultats est asynchrone et les tests peuvent déjà être en cours.

Pour le contrat d’interface, consultez la IGracefulStopTestExecutionCapability source.