Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Die Testplattform bietet wertvolle Dienste sowohl für das Testframework als auch für Erweiterungspunkte. Diese Dienste erfüllen allgemeine Anforderungen wie den Zugriff auf die Konfiguration, das Analysieren und Abrufen von Befehlszeilenargumenten, das Abrufen der Protokollierungsinstanz und den Zugriff auf das Protokollierungssystem sowie weitere Aufgaben. IServiceProvider
verwendet das Service-Locator-Muster für die Testplattform.
Dies IServiceProvider
wird direkt von der Basisklassenbibliothek abgeleitet.
namespace System
{
public interface IServiceProvider
{
object? GetService(Type serviceType);
}
}
Die Testplattform bietet praktische Erweiterungsmethoden für den Zugriff auf bekannte Dienstobjekte. Alle diese Methoden werden in einer statischen Klasse innerhalb des Microsoft.Testing.Platform.Services
Namespaces untergebracht.
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
}
Die meisten Registrierungsfactorys, die von Erweiterungspunkten angeboten werden, bieten Zugriff auf die IServiceProvider
: Wenn Sie beispielsweise das Testing Framework registrieren, wird IServiceProvider
als Parameter an die Factory-Methode übergeben.
ITestApplicationBuilder RegisterTestFramework(
Func<IServiceProvider, ITestFrameworkCapabilities> capabilitiesFactory,
Func<ITestFrameworkCapabilities, IServiceProvider, ITestFramework> adapterFactory);
Im vorherigen Code geben sowohl das capabilitiesFactory
als auch das adapterFactory
das IServiceProvider
als Parameter an.
Der IConfiguration
Dienst
Die IConfiguration
-Schnittstelle kann mithilfe von IServiceProvider
abgerufen werden und bietet Zugriff auf die Konfigurationseinstellungen des Testframeworks sowie aller Erweiterungspunkte. Standardmäßig werden diese Konfigurationen von den folgenden Quellen geladen:
- Umgebungsvariablen
- Eine JSON-Datei mit dem Namen
[assemblyName].testingplatformconfig.json
, die sich in der Nähe der Einstiegspunkt-Assembly befindet.
Die Reihenfolge der Rangfolge wird beibehalten, was bedeutet, dass die JSON-Datei nicht verarbeitet wird, wenn eine Konfiguration in den Umgebungsvariablen gefunden wird.
Die Schnittstelle ist ein simples Schlüssel-Wert-Paar von Zeichenfolgen.
public interface IConfiguration
{
string? this[string key] { get; }
}
JSON-Konfigurationsdatei
Die JSON-Datei folgt einer hierarchischen Struktur. Um auf untergeordnete Eigenschaften zuzugreifen, müssen Sie das Trennzeichen :
verwenden. Betrachten Sie z. B. eine Konfiguration für ein potenzielles Testframework wie:
{
"CustomTestingFramework": {
"DisableParallelism": true
}
}
Der Codeausschnitt sieht ungefähr wie folgt aus:
IServiceProvider serviceProvider = null; // Get the service provider...
var configuration = serviceProvider.GetConfiguration();
if (bool.TryParse(configuration["CustomTestingFramework:DisableParallelism"], out var value) && value is true)
{
// ...
}
Im Falle eines Arrays, wie zum Beispiel:
{
"CustomTestingFramework": {
"Engine": [
"ThreadPool",
"CustomThread"
]
}
}
Die Syntax für den Zugriff auf das erste Element ("ThreadPool") lautet:
IServiceProvider serviceProvider = null; // Get the service provider...
var configuration = serviceProvider.GetConfiguration();
var fistElement = configuration["CustomTestingFramework:Engine:0"];
Umgebungsvariablen
Das Trennzeichen :
funktioniert nicht mit hierarchischen Schlüsseln der Umgebungsvariablen auf allen Plattformen. __
, der doppelte Unterstrich, ist:
- Unterstützt von allen Plattformen. Das Trennzeichen
:
wird beispielsweise nicht von Bash unterstützt,__
hingegen schon. - automatisch durch
:
ersetzt.
Die Umgebungsvariable kann z. B. wie folgt festgelegt werden (dieses Beispiel gilt für Windows):
setx CustomTestingFramework__DisableParallelism=True
Sie können sich entscheiden, die Umgebungsvariablen-Konfigurationsquelle nicht zu verwenden, wenn Sie das ITestApplicationBuilder
erstellen:
var options = new TestApplicationOptions();
options.Configuration.ConfigurationSources.RegisterEnvironmentVariablesConfigurationSource = false;
var builder = await TestApplication.CreateBuilderAsync(args, options);
Der ICommandLineOptions
Dienst
Der ICommandLineOptions
Dienst wird verwendet, um Details zu den Befehlszeilenoptionen abzurufen, die die Plattform analysiert hat. Die verfügbaren APIs umfassen:
public interface ICommandLineOptions
{
bool IsOptionSet(string optionName);
bool TryGetOptionArgumentList(
string optionName,
out string[]? arguments);
}
Dies ICommandLineOptions
kann über bestimmte APIs abgerufen werden, z. B. den ICommandLineOptionsProvider, oder Sie können eine Instanz davon über die Erweiterungsmethode aus dem serviceProvider.GetCommandLineOptions()
abrufen.
ICommandLineOptions.IsOptionSet(string optionName)
: Mit dieser Methode können Sie überprüfen, ob eine bestimmte Option angegeben wurde. Wenn Sie das optionName
Präfix angeben, wird das --
Präfix weggelassen. Wenn der Benutzer zum Beispiel --myOption
eingibt, sollten Sie einfach myOption
übergeben.
ICommandLineOptions.TryGetOptionArgumentList(string optionName, out string[]? arguments)
: Mit dieser Methode können Sie überprüfen, ob eine bestimmte Option festgelegt wurde, und wenn ja, den entsprechenden Wert oder die entsprechenden Werte abrufen (wenn die Arität mehr als eine ist). Ähnlich wie im vorherigen Fall sollte die optionName
Angabe ohne Präfix --
erfolgen.
Der ILoggerFactory
Dienst
Die Testplattform verfügt über ein integriertes Protokollierungssystem, das eine Protokolldatei generiert. Sie können die Protokollierungsoptionen anzeigen, indem Sie den --help
Befehl ausführen.
Zu den Optionen, die Sie auswählen können, gehören:
--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-filelogger-synchronouswrite 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-output-fileprefix 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'
Vom Standpunkt des Codes aus betrachtet, müssen Sie, um Informationen zu protokollieren, die ILoggerFactory
von der IServiceProvider
abrufen.
Die ILoggerFactory
API lautet wie folgt:
public interface ILoggerFactory
{
ILogger CreateLogger(string categoryName);
}
public static class LoggerFactoryExtensions
{
public static ILogger<TCategoryName> CreateLogger<TCategoryName>(this ILoggerFactory factory);
}
Mit der Loggerfactory können Sie ein ILogger
Objekt mithilfe der CreateLogger
API erstellen. Es gibt auch eine praktische API, die ein generisches Argument akzeptiert, das als Kategoriename verwendet wird.
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);
}
Das ILogger
Objekt, das durch das ILoggerFactory
Objekt erstellt wird, bietet APIs zum Protokollieren von Informationen auf verschiedenen Ebenen. Diese Protokollierungsebenen umfassen:
public enum LogLevel
{
Trace,
Debug,
Information,
Warning,
Error,
Critical,
None,
}
Hier ist ein Beispiel für die Verwendung der Protokollierungs-API:
...
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}'");
}
// ...
Denken Sie daran, dass Sie, um unnötige Zuordnungen zu verhindern, mithilfe der API überprüfen sollten, ob das Level ILogger.IsEnabled(LogLevel)
ist.
Der IMessageBus
Dienst
Der Nachrichtenbusdienst ist der zentrale Mechanismus, der den Informationsaustausch zwischen dem Testframework und seinen Erweiterungen erleichtert.
Der Nachrichtenbus der Testplattform verwendet das Publish-Subscribe-Muster.
Die übergeordnete Struktur des gemeinsam genutzten Busses lautet wie folgt:
Wie im Diagramm dargestellt, das erweiterungen und ein Testframework enthält, gibt es zwei mögliche Aktionen: Das Pushen von Informationen an den Bus oder die Nutzung von Informationen vom Bus.
Das IMessageBus
erfüllt die Pushes-Aktion an den Bus und die API ist:
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; }
}
Berücksichtigen Sie die folgenden Details zu den Parametern:
IDataProducer
: DerIDataProducer
teilt dem Nachrichtenbus dieType
Informationen mit, die er liefern kann, und legt die Eigentümerschaft durch Vererbung von der Basisschnittstelle IExtension fest. Dies bedeutet, dass Sie daten nicht diskriminierend an den Nachrichtenbus übertragen können. Sie müssen den im Voraus erstellten Datentyp deklarieren. Wenn Sie unerwartete Daten übertragen, wird eine Ausnahme ausgelöst.IData
: Diese Schnittstelle dient als Platzhalter, in dem Sie nur beschreibende Details wie den Namen und eine Beschreibung angeben müssen. Die Schnittstelle offenbart absichtlich nicht viel über die Natur der Daten. Es bedeutet, dass das Testframework und Erweiterungen jede Art von Daten an den Bus übertragen können, und diese Daten können von jeder registrierten Erweiterung oder dem Testframework selbst genutzt werden.
Dieser Ansatz erleichtert die Weiterentwicklung des Informationsaustauschprozesses und verhindert unterbrechungslose Änderungen, wenn eine Erweiterung mit neuen Daten nicht vertraut ist. Es ermöglicht verschiedene Versionen von Erweiterungen und das Testframework, auf der Grundlage ihres gegenseitigen Verständnisses in Harmonie zu arbeiten.
Das gegenteilige Ende des Busses wird als Verbraucher bezeichnet, der einen bestimmten Datentyp abonniert und somit nutzen kann.
Von Bedeutung
Verwenden Sie immer await für den Aufruf von PublishAsync
. Wenn Sie dies nicht tun, könnte IData
möglicherweise nicht korrekt von der Testplattform und den Erweiterungen verarbeitet werden, was zu subtilen Fehlern führen könnte. Erst nachdem Sie von await zurückgekehrt sind, können Sie sicher sein, dass die IData
zur Verarbeitung auf dem Nachrichtenbus in die Warteschlange gestellt wurde. Unabhängig von dem Erweiterungspunkt, an dem Sie arbeiten, stellen Sie sicher, dass Sie alle PublishAsync
Aufrufe abgewartet haben, bevor Sie die Erweiterung beenden. Wenn Sie zum Beispiel testing framework
implementieren, sollten Sie Complete
für Anfragen erst dann aufrufen, wenn Sie alle PublishAsync
Aufrufe für diese spezielle Anfrage awaitiert haben.
Der IOutputDevice
Dienst
Die Testplattform kapselt die Idee eines Ausgabegeräts, sodass das Testframework und Erweiterungen Informationen darstellen können, indem jede Art von Daten an das aktuell genutzte Anzeigesystem übertragen wird.
Das traditionellste Beispiel für ein Ausgabegerät ist die Konsolenausgabe.
Hinweis
Während die Testplattform entwickelt wird, um benutzerdefinierte Ausgabegeräte zu unterstützen, ist dieser Erweiterungspunkt derzeit nicht verfügbar.
Um Daten an das Ausgabegerät zu übertragen, müssen Sie die IOutputDevice
von der IServiceProvider
abrufen.
Die API besteht aus:
public interface IOutputDevice
{
Task DisplayAsync(
IOutputDeviceDataProducer producer,
IOutputDeviceData data);
}
public interface IOutputDeviceDataProducer : IExtension
{
}
public interface IOutputDeviceData
{
}
IOutputDeviceDataProducer
erweitert IExtension
und stellt dem Ausgabegerät Informationen über den Absender bereit.
Das IOutputDeviceData
dient als Platzhalterschnittstelle. Das Dahinter IOutputDevice
liegende Konzept besteht darin, komplexere Informationen als nur farbigen Text aufzunehmen. Beispielsweise könnte es sich um ein komplexes Objekt handeln, das grafisch dargestellt werden kann.
Die Testplattform bietet standardmäßig ein herkömmliches farbiges Textmodell für das IOutputDeviceData
Objekt:
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; }
}
Hier ist ein Beispiel dafür, wie Sie den farbigen Text mit dem aktiven Ausgabegerät verwenden können:
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
}
});
Über die standardmäßige Verwendung von farbigem Text hinaus ist der Hauptvorteil von IOutputDevice
und IOutputDeviceData
, dass das Ausgabegerät völlig unabhängig ist und dem Benutzer unbekannt bleibt. Dies ermöglicht die Entwicklung komplexer Benutzeroberflächen. Beispielsweise ist es völlig machbar, eine Webanwendung in Echtzeit zu implementieren, die den Fortschritt von Tests anzeigt.
Der IPlatformInformation
Dienst
Stellt Informationen zur Plattform bereit, z. B. Name, Version, Commit-Hash und Builddatum.