Partager via


Microsoft.Testing.Platform Services

La plateforme de test offre des services précieux au cadre de test et aux points d'extension. Ces services répondent aux besoins courants tels que l’accès à la configuration, l’analyse et la récupération d’arguments de ligne de commande, l’obtention de la fabrique de journalisation et l’accès au système de journalisation, entre autres. IServiceProvider implémente le modèle de localisateur de service pour la plateforme de test.

IServiceProvider est dérivé directement de la bibliothèque de classes de base.

namespace System
{
    public interface IServiceProvider
    {
        object? GetService(Type serviceType);
    }
}

La plateforme de test offre des méthodes d’extension pratiques pour accéder à les objets de service connus. Toutes ces méthodes sont hébergées dans une classe statique dans l’espace Microsoft.Testing.Platform.Services de noms.

public static class ServiceProviderExtensions
{
    public static TService GetRequiredService<TService>(
        this IServiceProvider provider)

    public static TService? GetService<TService>(
        this IServiceProvider provider)

    public static IMessageBus GetMessageBus(
        this IServiceProvider serviceProvider)

    public static IConfiguration GetConfiguration(
        this IServiceProvider serviceProvider)

    public static ICommandLineOptions GetCommandLineOptions(
        this IServiceProvider serviceProvider)

    public static ILoggerFactory GetLoggerFactory(
        this IServiceProvider serviceProvider)

    public static IOutputDevice GetOutputDevice(
        this IServiceProvider serviceProvider)

    // ... and more
}

La plupart des fabriques d'enregistrement exposées par des points d’extension fournissent accès au IServiceProvider : par exemple, lors de l'enregistrement de l’infrastructure de test, le IServiceProvider est passé en tant que paramètre à la méthode de la fabrique.

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

Dans le code précédent, à la fois le capabilitiesFactory et le adapterFactory fournissent le IServiceProvider comme paramètre.

Le service IConfiguration

L’interface IConfiguration peut être récupérée à l’aide du IServiceProvider et fournit access aux paramètres de configuration de l’infrastructure de test et de tous les points d’extension. Par défaut, ces configurations sont chargées à partir des éléments suivants :

  • Variables d'environnement
  • Fichier JSON nommé [assemblyName].testingplatformconfig.json situé près de l’assembly de point d’entrée.

L’ordre de priorité est conservé, ce qui signifie que si une configuration est trouvée dans les variables d’environnement, le fichier JSON n’est pas traité.

L’interface est une paire clé-valeur simple de chaînes :

public interface IConfiguration
{
    string? this[string key] { get; }
}

Fichier de configuration JSON

Le fichier JSON suit une structure hiérarchique. Pour accéder aux propriétés des enfants, vous devez utiliser le séparateur :. Par exemple, considérez une configuration pour une infrastructure de test potentielle comme :

{
  "CustomTestingFramework": {
    "DisableParallelism": true
  }
}

L’extrait de code ressemblerait à ceci :

IServiceProvider serviceProvider = null; // Get the service provider...

var configuration = serviceProvider.GetConfiguration();

if (bool.TryParse(configuration["CustomTestingFramework:DisableParallelism"], out var value) && value is true)
{
    // ...
}

Dans le cas d’un tableau, comme :

{
  "CustomTestingFramework": {
    "Engine": [
      "ThreadPool",
      "CustomThread"
    ]
  }
}

La syntaxe pour accéder à l’élément premier (« ThreadPool ») est :

IServiceProvider serviceProvider = null; // Get the service provider...

var configuration = serviceProvider.GetConfiguration();

var fistElement = configuration["CustomTestingFramework:Engine:0"];

Variables d'environnement

Le séparateur : ne fonctionne pas avec les clés hiérarchiques des variables d’environnement sur toutes les plateformes. __, le trait de soulignement double, est :

  • Pris en charge par toutes les plateformes. Par exemple, le : séparateur n’est pas pris en charge par Bash, mais __ il l’est.
  • Automatiquement remplacé par un :

Par exemple, la variable d’environnement peut être définie comme suit (cet exemple s’applique à Windows) :

setx CustomTestingFramework__DisableParallelism=True

Vous pouvez choisir de ne pas utiliser la source de configuration des variables d’environnement lors de la création des ITestApplicationBuilderéléments suivants :

var options = new TestApplicationOptions();

options.Configuration.ConfigurationSources.RegisterEnvironmentVariablesConfigurationSource = false;

var builder = await TestApplication.CreateBuilderAsync(args, options);

Le service ICommandLineOptions

Le ICommandLineOptions service est utilisé pour récupérer des détails concernant les options de ligne de commande analysées par la plateforme. Les API disponibles sont les suivantes :

public interface ICommandLineOptions
{
    bool IsOptionSet(string optionName);

    bool TryGetOptionArgumentList(
        string optionName, 
        out string[]? arguments);
}

Certaines API, telles que ICommandLineOptions, permettent d’obtenir le ou bien de récupérer une instance de celui-ci à partir de IServiceProvider via la méthode d’extension serviceProvider.GetCommandLineOptions().

ICommandLineOptions.IsOptionSet(string optionName): cette méthode vous permet de vérifier si une option spécifique a été spécifiée. Lorsque vous spécifiez le optionNamepréfixe, omettez le -- préfixe. Par exemple, si l’utilisateur entre --myOption, vous devez simplement passer myOption.

ICommandLineOptions.TryGetOptionArgumentList(string optionName, out string[]? arguments): cette méthode vous permet de vérifier si une option spécifique a été définie et, le cas échéant, de récupérer la valeur ou les valeurs correspondantes (si la arité est plusieurs). Comme dans le cas précédent, la optionName valeur doit être fournie sans préfixe -- .

Le service ILoggerFactory

La plateforme de test est fournie avec un système de journalisation intégré qui génère un fichier journal. Vous pouvez afficher les options de journalisation en exécutant la --help commande. Les options que vous pouvez choisir sont les suivantes :

--diagnostic                             Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag
--diagnostic-synchronous-write Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution.
--diagnostic-output-directory            Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory.
--diagnostic-file-prefix           Prefix for the log file name that will replace '[log]_.'
--diagnostic-verbosity                   Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical'

Du point de vue du codage, pour journaliser des informations, vous devez obtenir le ILoggerFactory à partir du IServiceProvider. L’API ILoggerFactory est la suivante :

public interface ILoggerFactory
{
    ILogger CreateLogger(string categoryName);
}

public static class LoggerFactoryExtensions
{
    public static ILogger<TCategoryName> CreateLogger<TCategoryName>(this ILoggerFactory factory);
}

La fabrique de loggers permet de créer un objet ILogger en utilisant l'API CreateLogger. Il existe également une API pratique qui accepte un argument générique, qui sera utilisé comme nom de catégorie.

public interface ILogger
{
    Task LogAsync<TState>(
        LogLevel logLevel, 
        TState state, 
        Exception? exception, 
        Func<TState, Exception?, string> formatter);

    void Log<TState>(
        LogLevel logLevel,
        TState state, 
        Exception? exception, 
        Func<TState, Exception?, string> formatter);

    bool IsEnabled(LogLevel logLevel);
}

public interface ILogger<out TCategoryName> : ILogger
{
}

public static class LoggingExtensions
{
    public static Task LogCriticalAsync(this ILogger logger, string message);
    public static Task LogDebugAsync(this ILogger logger, string message);
    public static Task LogErrorAsync(this ILogger logger, Exception ex);
    public static Task LogErrorAsync(this ILogger logger, string message, Exception ex);
    public static Task LogErrorAsync(this ILogger logger, string message);
    public static Task LogInformationAsync(this ILogger logger, string message);
    public static Task LogTraceAsync(this ILogger logger, string message);
    public static Task LogWarningAsync(this ILogger logger, string message);
    public static void LogCritical(this ILogger logger, string message);
    public static void LogDebug(this ILogger logger, string message);
    public static void LogError(this ILogger logger, Exception ex);
    public static void LogError(this ILogger logger, string message, Exception ex);
    public static void LogError(this ILogger logger, string message);
    public static void LogInformation(this ILogger logger, string message);
    public static void LogTrace(this ILogger logger, string message);
    public static void LogWarning(this ILogger logger, string message);
}

L’objet ILogger , créé par le ILoggerFactory, offre des API pour la journalisation des informations à différents niveaux. Ces niveaux de journaux sont les suivants :

public enum LogLevel
{
    Trace,
    Debug,
    Information,
    Warning,
    Error,
    Critical,
    None,
}

Voici un exemple de la façon dont vous pouvez utiliser l’API de journalisation :

...
IServiceProvider provider = null; // Get the service provider...

var factory = provider.GetLoggerFactory();

var logger = factory.CreateLogger<TestingFramework>();

// ...

if (logger.IsEnabled(LogLevel.Information))
{
    await logger.LogInformationAsync(
        $"Executing request of type '{context.Request}'");
}

// ...

N’oubliez pas que pour empêcher l’allocation inutile, vous devez vérifier si le niveau est activé à l’aide de l’API ILogger.IsEnabled(LogLevel) .

Le service IMessageBus

Le bus de messages est le mécanisme central qui facilite l'échange d'informations entre l’infrastructure de test et ses extensions.

Le bus de messages de la plateforme de test utilise le modèle de publication-abonnement.

La structure globale du bus partagé est la suivante :

Image représentant les interactions des différentes extensions avec le bus de messages.

Comme illustré dans le diagramme, qui inclut une extension et une infrastructure de test, il existe deux actions potentielles : envoyer des informations vers le bus ou consommer des informations à partir du bus.

Le IMessageBus satisfait l'action de push au bus et l'API est :

public interface IMessageBus
{
    Task PublishAsync(
        IDataProducer dataProducer, 
        IData data);
}

public interface IDataProducer : IExtension
{
    Type[] DataTypesProduced { get; }
}

public interface IData
{
    string DisplayName { get; }
    string? Description { get; }
}

Tenez compte des détails suivants sur les paramètres :

  • IDataProducer : Le IDataProducer communique au bus de messages les Type informations qu'il peut fournir et établit la propriété par héritage de l'interface de base IExtension. Cela implique que vous ne pouvez pas envoyer de données sans discrimination vers le bus de messages ; vous devez déclarer le type de données produit à l’avance. Si vous envoyez des données inattendues, une exception est déclenchée.

  • IData: cette interface sert d’espace réservé où vous devez uniquement fournir des détails descriptifs tels que le nom et une description. L’interface ne révèle pas beaucoup sur la nature des données, ce qui est intentionnel. Cela implique que l’infrastructure de test et les extensions peuvent envoyer (push) n’importe quel type de données vers le bus, et ces données peuvent être consommées par n’importe quelle extension inscrite ou l’infrastructure de test elle-même.

Cette approche facilite l’évolution du processus d'échange d'informations, ce qui empêche les changements disruptifs lorsqu'une extension n’est pas familiarisée avec les nouvelles données. Il permet à différentes versions d’extensions et à l’infrastructure de test de fonctionner en harmonie, en fonction de leur compréhension mutuelle.

L’extrémité opposée du bus est appelée un consommateur, qui est abonné à un type spécifique de données et peut donc le consommer.

Importante

Toujours utiliser await lors de l'appel à PublishAsync. Si vous ne le faites pas, le traitement IData risque de ne pas être correctement effectué par la plateforme de test et les extensions, ce qui pourrait entraîner des bogues subtils. Ce n'est qu'après le retour de l'attente que vous pouvez être assuré que le IData a été mis en file d'attente pour traitement sur le bus de messages. Quel que soit le point d’extension sur lequel vous travaillez, vérifiez que vous avez attendu tous les PublishAsync appels avant de quitter l’extension. Par exemple, si vous implémentez le testing framework, vous ne devez pas appeler Complete sur les requêtes tant que vous n'avez pas attendu tous les appels PublishAsync pour cette demande spécifique.

Le service IOutputDevice

La plateforme de test encapsule l’idée d’un appareil de sortie, ce qui permet au framework de test et aux extensions de présenter des informations en transmettant tout type de données au système d’affichage actuellement utilisé.

L’exemple le plus traditionnel d’un appareil de sortie est la sortie de la console.

Remarque

Bien que la plateforme de test soit conçue pour prendre en charge les appareils de sortie personnalisés, actuellement, ce point d’extension n’est pas disponible.

Pour transmettre des données à l’appareil de sortie, vous devez obtenir IOutputDevice à partir du IServiceProvider.

L’API se compose des éléments suivants :

public interface IOutputDevice
{
    Task DisplayAsync(
        IOutputDeviceDataProducer producer, 
        IOutputDeviceData data);
}

public interface IOutputDeviceDataProducer : IExtension
{
}

public interface IOutputDeviceData
{
}

L’IOutputDeviceDataProducer prolonge le IExtension et fournit des informations sur l’expéditeur à l’appareil de sortie.

Le service IOutputDeviceData sert d'interface de remplacement. Le concept derrière IOutputDevice consiste à prendre en charge des informations plus complexes que du texte coloré. Par exemple, il peut s’agir d’un objet complexe qui peut être représenté graphiquement.

La plateforme de test, par défaut, offre un modèle de texte coloré traditionnel pour l’objet IOutputDeviceData :

public class TextOutputDeviceData : IOutputDeviceData
{
    public TextOutputDeviceData(string text)
    public string Text { get; }
}

public sealed class FormattedTextOutputDeviceData : TextOutputDeviceData
{
    public FormattedTextOutputDeviceData(string text)
    public IColor? ForegroundColor { get; init; }
    public IColor? BackgroundColor { get; init; }
}

public sealed class SystemConsoleColor : IColor
{
    public ConsoleColor ConsoleColor { get; init; }
}

Voici un exemple de la façon dont vous pouvez utiliser le texte coloré avec l’appareil de sortie actif :

IServiceProvider provider = null; // Get the service provider...

var outputDevice = provider.GetOutputDevice();

await outputDevice.DisplayAsync(
    this, 
    new FormattedTextOutputDeviceData($"TestingFramework version '{Version}' running tests with parallelism of {_dopValue}")
    {
        ForegroundColor = new SystemConsoleColor
        {
            ConsoleColor = ConsoleColor.Green
        }
    });

Au-delà de l’utilisation standard du texte coloré, l’avantage principal de IOutputDevice et IOutputDeviceData est que l’appareil de sortie est entièrement indépendant et inconnu de l’utilisateur. Cela permet le développement d’interfaces utilisateur complexes. Par exemple, il est tout à fait possible d’implémenter une application web en temps réel qui affiche la progression des tests.

Le service IPlatformInformation

Fournit des informations sur la plateforme, comme le nom, la version, le hachage de validation et la date de génération.