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.
In diesem Artikel werden die Erweiterbarkeitspunkte für Microsoft.Testing.Platform über das Testframework selbst hinaus behandelt. Informationen zur Erstellung von Testframeworks finden Sie unter Erstellen eines Testframeworks.
Die vollständige Zusammenfassung des Erweiterungspunkts und prozessinterne/out-of-process-Konzepte finden Sie unter Erstellen von benutzerdefinierten Erweiterungen.
Andere Erweiterungspunkte
Die Testplattform bietet zusätzliche Erweiterungspunkte, mit denen Sie das Verhalten der Plattform und des Testframeworks anpassen können. Diese Erweiterungspunkte sind optional und können verwendet werden, um die Testerfahrung zu verbessern.
ICommandLineOptionsProvider-Erweiterungen
Hinweis
Wenn Sie diese API erweitern, ist die benutzerdefinierte Erweiterung sowohl innerhalb als auch außerhalb des Testhostprozesses vorhanden.
Wie im Abschnitt zur Architektur erläutert, erstellen Sie im ersten Schritt den ITestApplicationBuilder, um das Testframework und die Erweiterungen zu registrieren.
var builder = await TestApplication.CreateBuilderAsync(args);
Die CreateBuilderAsync-Methode akzeptiert ein Array von Zeichenfolgen (string[]) namens args. Diese Argumente können verwendet werden, um Befehlszeilenoptionen an alle Komponenten der Testplattform zu übergeben (einschließlich integrierter Komponenten, Testframeworks und Erweiterungen), sodass ihr Verhalten angepasst werden kann.
In der Regel werden die in der Main(string[] args)-Standardmethode empfangenen Argumente übergeben. Wenn sich die Hostumgebung unterscheidet, kann jedoch eine beliebige Liste von Argumenten übergeben werden.
Argumente müssen als Präfix einen doppelten Bindestrich -- haben. Beispiel: --filter.
Wenn eine Komponente wie ein Testframework oder ein Erweiterungspunkt benutzerdefinierte Befehlszeilenoptionen bereitstellen soll, ist dies durch Implementieren der ICommandLineOptionsProvider-Schnittstelle möglich. Diese Implementierung kann dann wie gezeigt mit ITestApplicationBuilder über die Registrierungsfabrik der CommandLine-Eigenschaft registriert werden.
builder.CommandLine.AddProvider(
static () => new CustomCommandLineOptions());
Im Beispiel ist CustomCommandLineOptions eine Implementierung der ICommandLineOptionsProvider-Schnittstelle. Diese Schnittstelle umfasst die folgenden Member und Datentypen:
public interface ICommandLineOptionsProvider : IExtension
{
IReadOnlyCollection<CommandLineOption> GetCommandLineOptions();
Task<ValidationResult> ValidateOptionArgumentsAsync(
CommandLineOption commandOption,
string[] arguments);
Task<ValidationResult> ValidateCommandLineOptionsAsync(
ICommandLineOptions commandLineOptions);
}
public sealed class CommandLineOption
{
public string Name { get; }
public string Description { get; }
public ArgumentArity Arity { get; }
public bool IsHidden { get; }
// ...
}
public interface ICommandLineOptions
{
bool IsOptionSet(string optionName);
bool TryGetOptionArgumentList(
string optionName,
out string[]? arguments);
}
Wie Sie sehen, erweitert die ICommandLineOptionsProvider-Schnittstelle die IExtension-Schnittstelle. Daher können Sie diese Erweiterung wie jede andere mithilfe der IExtension.IsEnabledAsync-API aktivieren oder deaktivieren.
Die Ausführungsreihenfolge von ICommandLineOptionsProvider lautet wie folgt:
Sehen wir uns die APIs und ihre Funktion/Zweck genauer an:
ICommandLineOptionsProvider.GetCommandLineOptions(): Diese Methode wird verwendet, um alle Optionen abzurufen, die die Komponente bietet. Für jede CommandLineOption müssen die folgenden Eigenschaften angegeben werden:
string name: Dies ist der Name der Option (dargestellt ohne Gedankenstrich). Die Option filter wird von Benutzern beispielsweise als --filter verwendet.
string description: Dies ist eine Beschreibung der Option. Sie wird angezeigt, wenn Benutzer --help als Argument an den Anwendungs-Generator übergeben.
ArgumentArity arity: Die Stelligkeit einer Option ist die Anzahl der Werte, die mitgegeben werden können, wenn diese Option oder dieser Befehl angegeben ist. Folgende Stelligkeiten sind derzeit verfügbar:
-
Zero: Repräsentiert eine Stelligkeit von null für Argumente. -
ZeroOrOne: Stellt eine Argumentstelligkeit von Null oder eins dar. -
ZeroOrMore: Stellt eine Anzahl der Argumente von Null oder mehr dar. -
OneOrMore: Stellt eine Stelligkeit der Argumente von 1 oder mehr dar. -
ExactlyOne: Stellt eine Stelligkeit der Argumente von genau 1 dar.
Beispiele finden Sie in der Tabelle zur Argumentarität von System.CommandLine.
bool isHidden: Diese Eigenschaft gibt an, dass die Option zur Verwendung verfügbar ist, aber nicht in der Beschreibung angezeigt wird, wenn --help aufgerufen wird.
ICommandLineOptionsProvider.ValidateOptionArgumentsAsync: Diese Methode wird verwendet, um das vom Benutzer bereitgestellte Argument zu überprüfen.
Wenn Sie beispielsweise einen Parameter namens --dop haben, der den Grad der Parallelität für das benutzerdefinierte Testframework darstellt, kann ein Benutzer --dop 0 eingeben. In diesem Szenario wäre der Wert 0 ungültig, da ein Parallelitätsgrad von mindestens 1 erwartet wird. Mithilfe von ValidateOptionArgumentsAsync können Sie eine Vorabüberprüfung durchführen und bei Bedarf eine Fehlermeldung zurückgeben.
Eine mögliche Implementierung für das obige Beispiel könnte wie folgt aussehen:
public Task<ValidationResult> ValidateOptionArgumentsAsync(
CommandLineOption commandOption,
string[] arguments)
{
if (commandOption.Name == "dop")
{
if (!int.TryParse(arguments[0], out int dopValue) || dopValue <= 0)
{
return ValidationResult.InvalidTask("--dop must be a positive integer");
}
}
return ValidationResult.ValidTask;
}
ICommandLineOptionsProvider.ValidateCommandLineOptionsAsync: Diese Methode wird zuletzt aufgerufen und ermöglicht die Durchführung einer globalen Kohärenzprüfung.
Angenommen, unser Testframework bietet eine Funktion, mit der ein Testergebnisbericht generiert und in einer Datei gespeichert werden kann. Auf dieses Feature wird mithilfe der Option --generatereport zugegriffen, und der Dateiname wird mit --reportfilename myfile.rep angegeben. Wenn ein Benutzer in diesem Szenario die Option --generatereport, aber keinen Dateinamen angibt, sollte die Überprüfung fehlschlagen, da der Bericht ohne einen Dateinamen nicht generiert werden kann.
Eine mögliche Implementierung für das obige Beispiel könnte wie folgt aussehen:
public Task<ValidationResult> ValidateCommandLineOptionsAsync(ICommandLineOptions commandLineOptions)
{
bool generateReportEnabled = commandLineOptions.IsOptionSet(GenerateReportOption);
bool reportFileName = commandLineOptions.TryGetOptionArgumentList(ReportFilenameOption, out string[]? _);
return (generateReportEnabled || reportFileName) && !(generateReportEnabled && reportFileName)
? ValidationResult.InvalidTask("Both `--generatereport` and `--reportfilename` need to be provided simultaneously.")
: ValidationResult.ValidTask;
}
Beachten Sie, dass die ValidateCommandLineOptionsAsync-Methode den ICommandLineOptions-Dienst bereitstellt, der zum Abrufen der Argumentinformationen verwendet wird, die von der Plattform selbst analysiert werden.
ITestSessionLifetimeHandler-Erweiterungen
ITestSessionLifeTimeHandler ist eine prozessinterne Erweiterung, die die Ausführung von Code vor und nach der Testsitzung ermöglicht.
Verwenden Sie die folgende API, um eine benutzerdefinierte ITestSessionLifeTimeHandler-Schnittstelle zu registrieren:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddTestSessionLifetimeHandle(
static serviceProvider => new CustomTestSessionLifeTimeHandler());
Die Factory nutzt den IServiceProvider, um Zugriff auf die Sammlung von Diensten zu erhalten, die die Testplattform bietet.
Von Bedeutung
Die Registrierungsreihenfolge ist wichtig, da die APIs in der Reihenfolge aufgerufen werden, in der sie registriert wurden.
Die ITestSessionLifeTimeHandler-Schnittstelle umfasst die folgenden Methoden:
public interface ITestSessionLifetimeHandler : ITestHostExtension
{
Task OnTestSessionStartingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken);
Task OnTestSessionFinishingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken);
}
public readonly struct SessionUid(string value)
{
public string Value { get; } = value;
}
public interface ITestHostExtension : IExtension
{
}
ITestSessionLifetimeHandler ist ein Typ von ITestHostExtension, der als Basis für alle Testhosterweiterungen dient. Wie alle anderen Erweiterungspunkte erbt er ebenfalls von IExtension. Daher können Sie diese Erweiterung wie jede andere mithilfe der IExtension.IsEnabledAsync-API aktivieren oder deaktivieren.
Beachten Sie für diese API die folgenden Details:
OnTestSessionStartingAsync: Diese Methode wird vor Beginn der Testsitzung aufgerufen und empfängt das SessionUid-Objekt, das einen nicht transparenten Bezeichner für die aktuelle Testsitzung bereitstellt.
OnTestSessionFinishingAsync: Diese Methode wird nach Abschluss der Testsitzung aufgerufen, um sicherzustellen, dass das Testframework die Ausführung aller Tests abgeschlossen und alle relevanten Daten an die Plattform gemeldet hat. In dieser Methode verwendet die Erweiterung in der Regel den IMessageBus, um benutzerdefinierte Ressourcen oder Daten an den freigegebenen Plattformbus zu übermitteln. Diese Methode kann auch beliebigen prozessexternen Erweiterungen den Abschluss der Testsitzung signalisieren.
Beide APIs akzeptieren zudem ein CancellationToken, das von der Erweiterung berücksichtigt werden muss.
Wenn Ihre Erweiterung eine intensive Initialisierung erfordert und Sie das async/await-Muster verwenden müssen, finden Sie unter Async extension initialization and cleanup weitere Informationen. Um den Status zwischen Erweiterungspunkten zu teilen, können Sie den Abschnitt CompositeExtensionFactory<T> einsehen.
ITestApplicationLifecycleCallbacks-Erweiterungen
ITestApplicationLifecycleCallbacks ist eine prozessinterne Erweiterung, die die Ausführung von Code vor allem ermöglicht, es ist, als hätte man Zugriff auf die erste Zeile der hypothetischen main des Testhosts.
Verwenden Sie die folgende API, um eine benutzerdefinierte ITestApplicationLifecycleCallbacks-Erweiterung zu registrieren:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddTestApplicationLifecycleCallbacks(
static serviceProvider
=> new CustomTestApplicationLifecycleCallbacks());
Die Factory nutzt den IServiceProvider, um Zugriff auf die Sammlung von Diensten zu erhalten, die die Testplattform bietet.
Von Bedeutung
Die Registrierungsreihenfolge ist wichtig, da die APIs in der Reihenfolge aufgerufen werden, in der sie registriert wurden.
Die ITestApplicationLifecycleCallbacks-Schnittstelle umfasst die folgenden Methoden:
public interface ITestApplicationLifecycleCallbacks : ITestHostExtension
{
Task BeforeRunAsync(CancellationToken cancellationToken);
Task AfterRunAsync(
int exitCode,
CancellationToken cancellation);
}
public interface ITestHostExtension : IExtension
{
}
ITestApplicationLifecycleCallbacks ist ein Typ von ITestHostExtension, der als Basis für alle Testhosterweiterungen dient. Wie alle anderen Erweiterungspunkte erbt er ebenfalls von IExtension. Daher können Sie diese Erweiterung wie jede andere mithilfe der IExtension.IsEnabledAsync-API aktivieren oder deaktivieren.
BeforeRunAsync: Diese Methode dient als anfänglicher Kontaktpunkt für den Testhost und ist für eine prozessinterne Erweiterung die erste Möglichkeit zum Ausführen eines Features. Sie wird in der Regel verwendet, um eine Verbindung mit entsprechenden prozessexternen Erweiterungen herzustellen, wenn ein Feature für die Ausführung in beiden Umgebungen konzipiert ist.
Das integrierte Feature zum Erstellen eines Speicherabbilds bei Nichtreagieren besteht beispielsweise aus prozessinternen und prozessexternen Erweiterungen, und diese Methode wird verwendet, um Informationen mit der prozessexternen Komponente der Erweiterung auszutauschen.
AfterRunAsync: Diese Methode ist der letzte Aufruf vor dem Beenden der int ITestApplication.RunAsync()-Methode und stellt den exit code bereit. Sie sollte ausschließlich verwendet werden, um Bereinigungsaufgaben durchzuführen und alle entsprechenden prozessexternen Erweiterungen darüber zu benachrichtigen, dass der Testhost beendet wird.
Beide APIs akzeptieren zudem ein CancellationToken, das von der Erweiterung berücksichtigt werden muss.
IDataConsumer-Erweiterungen
IDataConsumer ist eine prozessinterne Erweiterung, die in der Lage ist, IData-Informationen zu abonnieren und zu empfangen, die vom Testframework und seinen Erweiterungen an den IMessageBus gesendet werden.
Dieser Erweiterungspunkt ist entscheidend, da er Entwicklern das Sammeln und Verarbeiten aller Informationen ermöglicht, die während einer Testsitzung generiert werden.
Verwenden Sie die folgende API, um eine benutzerdefinierte IDataConsumer-Erweiterung zu registrieren:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddDataConsumer(
static serviceProvider => new CustomDataConsumer());
Die Factory nutzt den IServiceProvider, um Zugriff auf die Sammlung von Diensten zu erhalten, die die Testplattform bietet.
Von Bedeutung
Die Registrierungsreihenfolge ist wichtig, da die APIs in der Reihenfolge aufgerufen werden, in der sie registriert wurden.
Die IDataConsumer-Schnittstelle umfasst die folgenden Methoden:
public interface IDataConsumer : ITestHostExtension
{
Type[] DataTypesConsumed { get; }
Task ConsumeAsync(
IDataProducer dataProducer,
IData value,
CancellationToken cancellationToken);
}
public interface IData
{
string DisplayName { get; }
string? Description { get; }
}
IDataConsumer ist ein Typ von ITestHostExtension, der als Basis für alle Testhosterweiterungen dient. Wie alle anderen Erweiterungspunkte erbt er ebenfalls von IExtension. Daher können Sie diese Erweiterung wie jede andere mithilfe der IExtension.IsEnabledAsync-API aktivieren oder deaktivieren.
DataTypesConsumed: Diese Eigenschaft gibt eine Liste von Type zurück, die von dieser Erweiterung verwendet werden soll. Sie entspricht IDataProducer.DataTypesProduced. Insbesondere kann ein IDataConsumer problemlos mehrere Typen aus verschiedenen IDataProducer-Instanzen abonnieren.
ConsumeAsync: Diese Methode wird ausgelöst, wenn Daten eines Typs, den der aktuelle Consumer abonniert hat, an den IMessageBus gepusht werden. Empfängt den IDataProducer, um Details zum Erzeuger der Datennutzlast sowie die IData-Nutzdaten selbst bereitzustellen. Wie Sie sehen, ist IData eine generische Platzhalterschnittstelle, die allgemeine informative Daten enthält. Die Möglichkeit, verschiedene IData-Typen zu pushen, bedeutet, dass der Consumer den Typ selbst aktivieren muss, um ihn in den richtigen Typ umzuwandeln und auf die spezifischen Informationen zuzugreifen.
Eine Beispielimplementierung eines Verbrauchers, der die von einem TestNodeUpdateMessage erzeugte Ausgabe eines Testing-Frameworks auswerten will, könnte wie folgt aussehen:
internal class CustomDataConsumer : IDataConsumer, IOutputDeviceDataProducer
{
public Type[] DataTypesConsumed => new[] { typeof(TestNodeUpdateMessage) };
...
public Task ConsumeAsync(
IDataProducer dataProducer,
IData value,
CancellationToken cancellationToken)
{
var testNodeUpdateMessage = (TestNodeUpdateMessage)value;
switch (testNodeUpdateMessage.TestNode.Properties.Single<TestNodeStateProperty>())
{
case InProgressTestNodeStateProperty _:
{
...
break;
}
case PassedTestNodeStateProperty _:
{
...
break;
}
case FailedTestNodeStateProperty failedTestNodeStateProperty:
{
...
break;
}
case SkippedTestNodeStateProperty _:
{
...
break;
}
...
}
return Task.CompletedTask;
}
...
}
Zu guter Letzt akzeptiert die API ein CancellationToken, das von der Erweiterung berücksichtigt werden muss.
Von Bedeutung
Es ist entscheidend, die Nutzdaten direkt innerhalb der ConsumeAsync-Methode zu verarbeiten. Der IMessageBus kann sowohl die synchrone als auch die asynchrone Verarbeitung verwalten und die Ausführung mit dem Testframework koordinieren. Zum Zeitpunkt der Erstellung dieses Dokuments ist der Nutzungs- bzw. Verbrauchsprozess vollständig asynchron und blockiert IMessageBus.Push nicht. Dies ist jedoch ein Implementierungsdetail, das sich aufgrund künftiger Anforderungen in Zukunft ändern kann. Die Plattform stellt jedoch sicher, dass diese Methode mindestens einmal aufgerufen wird, wodurch eine komplexe Synchronisierung nicht erforderlich ist und die Skalierbarkeit der Konsumenten gewährleistet wird.
Warnung
Wenn Sie IDataConsumer in Verbindung mit ITestHostProcessLifetimeHandler innerhalb eines zusammengesetzten Erweiterungspunkts verwenden, ist es wichtig, alle Daten zu ignorieren, die nach der Ausführung von ITestSessionLifetimeHandler.OnTestSessionFinishingAsync empfangen werden. Die OnTestSessionFinishingAsync-Methode ist die letzte Möglichkeit, gesammelte Daten zu verarbeiten und neue Informationen an den IMessageBus zu übermitteln. Daher können alle Daten, die nach diesem Punkt genutzt werden, nicht von der Erweiterung verwendet werden.
Wenn Ihre Erweiterung eine intensive Initialisierung erfordert und Sie das async/await-Muster verwenden müssen, finden Sie unter Async extension initialization and cleanup weitere Informationen. Um den Status zwischen Erweiterungspunkten zu teilen, können Sie den Abschnitt CompositeExtensionFactory<T> einsehen.
ITestHostEnvironmentVariableProvider-Erweiterungen
ITestHostEnvironmentVariableProvider ist eine prozessexterne Erweiterung, mit der Sie benutzerdefinierte Umgebungsvariablen für den Testhost einrichten können. Die Verwendung dieses Erweiterungspunkts stellt sicher, dass die Testplattform einen neuen Host mit den entsprechenden Umgebungsvariablen initiiert (siehe hierzu den Abschnitt zur Architektur).
Verwenden Sie die folgende API, um eine benutzerdefinierte ITestHostEnvironmentVariableProvider-Schnittstelle zu registrieren:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHostControllers.AddEnvironmentVariableProvider(
static serviceProvider => new CustomEnvironmentVariableForTestHost());
Die Factory nutzt den IServiceProvider, um Zugriff auf die Sammlung von Diensten zu erhalten, die die Testplattform bietet.
Von Bedeutung
Die Registrierungsreihenfolge ist wichtig, da die APIs in der Reihenfolge aufgerufen werden, in der sie registriert wurden.
Die ITestHostEnvironmentVariableProvider-Schnittstelle umfasst die folgenden Methoden und Typen:
public interface ITestHostEnvironmentVariableProvider : ITestHostControllersExtension, IExtension
{
Task UpdateAsync(IEnvironmentVariables environmentVariables);
Task<ValidationResult> ValidateTestHostEnvironmentVariablesAsync(
IReadOnlyEnvironmentVariables environmentVariables);
}
public interface IEnvironmentVariables : IReadOnlyEnvironmentVariables
{
void SetVariable(EnvironmentVariable environmentVariable);
void RemoveVariable(string variable);
}
public interface IReadOnlyEnvironmentVariables
{
bool TryGetVariable(
string variable,
[NotNullWhen(true)] out OwnedEnvironmentVariable? environmentVariable);
}
public sealed class OwnedEnvironmentVariable : EnvironmentVariable
{
public IExtension Owner { get; }
public OwnedEnvironmentVariable(
IExtension owner,
string variable,
string? value,
bool isSecret,
bool isLocked);
}
public class EnvironmentVariable
{
public string Variable { get; }
public string? Value { get; }
public bool IsSecret { get; }
public bool IsLocked { get; }
}
ITestHostEnvironmentVariableProvider ist ein Typ von ITestHostControllersExtension, der als Basis für alle Testhostcontroller-Erweiterungen dient. Wie alle anderen Erweiterungspunkte erbt er ebenfalls von IExtension. Daher können Sie diese Erweiterung wie jede andere mithilfe der IExtension.IsEnabledAsync-API aktivieren oder deaktivieren.
Berücksichtigen Sie für diese API die folgenden Details:
UpdateAsync: Diese Aktualisierungs-API stellt eine Instanz des IEnvironmentVariables-Objekts bereit, über die Sie die Methoden SetVariable oder RemoveVariable aufrufen können. Wenn Sie SetVariable verwenden, müssen Sie ein Objekt vom Typ EnvironmentVariable übergeben, das die folgenden Angaben erfordert:
-
Variable: Der Name der Umgebungsvariable. -
Value: Der Wert der Umgebungsvariable. -
IsSecret: Gibt an, ob die Umgebungsvariable vertrauliche Informationen enthält, die nicht protokolliert werden oder nicht überTryGetVariablezugänglich sein sollen. -
IsLocked: Legt fest, ob andereITestHostEnvironmentVariableProvider-Erweiterungen diesen Wert ändern können.
ValidateTestHostEnvironmentVariablesAsync: Diese Methode wird aufgerufen, nachdem alle UpdateAsync-Methoden der registrierten ITestHostEnvironmentVariableProvider-Instanzen aufgerufen wurden. Sie ermöglicht es Ihnen, die korrekte Einrichtung der Umgebungsvariablen zu überprüfen. Sie akzeptiert ein Objekt, das die IReadOnlyEnvironmentVariables-Schnittstelle implementiert, die die TryGetVariable-Methode zum Abrufen bestimmter Umgebungsvariableninformationen mit dem OwnedEnvironmentVariable-Objekttyp bereitstellt. Nach der Überprüfung geben Sie ein ValidationResult zurück, das alle Fehlerursachen enthält.
Hinweis
Die Testplattform implementiert und registriert standardmäßig den SystemEnvironmentVariableProvider. Dieser Anbieter lädt alle aktuellen Umgebungsvariablen. Als erster registrierter Anbieter wird er zuerst ausgeführt und gewährt allen anderen ITestHostEnvironmentVariableProvider-Benutzererweiterungen den Zugriff auf die Standardumgebungsvariablen.
Wenn Ihre Erweiterung eine intensive Initialisierung erfordert und Sie das async/await-Muster verwenden müssen, finden Sie unter Async extension initialization and cleanup weitere Informationen. Um den Status zwischen Erweiterungspunkten zu teilen, können Sie den Abschnitt CompositeExtensionFactory<T> einsehen.
ITestHostProcessLifetimeHandler-Erweiterungen
ITestHostProcessLifetimeHandler ist eine prozessexterne Erweiterung, mit der Sie den Testhostprozess aus externer Sicht beobachten können. Dadurch wird sichergestellt, dass sich durch den getesteten Code verursachte mögliche Abstürze oder ein Hängenbleiben nicht auf Ihre Erweiterung auswirken. Wenn Sie diesen Erweiterungspunkt verwenden, wird die Testplattform aufgefordert, einen neuen Host zu initiieren (siehe hierzu den Abschnitt zur Architektur).
Verwenden Sie die folgende API, um eine benutzerdefinierte ITestHostProcessLifetimeHandler-Schnittstelle zu registrieren:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHostControllers.AddProcessLifetimeHandler(
static serviceProvider => new CustomMonitorTestHost());
Die Factory nutzt den IServiceProvider, um Zugriff auf die Sammlung von Diensten zu erhalten, die die Testplattform bietet.
Von Bedeutung
Die Registrierungsreihenfolge ist wichtig, da die APIs in der Reihenfolge aufgerufen werden, in der sie registriert wurden.
Die ITestHostProcessLifetimeHandler-Schnittstelle umfasst die folgenden Methoden:
public interface ITestHostProcessLifetimeHandler : ITestHostControllersExtension
{
Task BeforeTestHostProcessStartAsync(CancellationToken cancellationToken);
Task OnTestHostProcessStartedAsync(
ITestHostProcessInformation testHostProcessInformation,
CancellationToken cancellation);
Task OnTestHostProcessExitedAsync(
ITestHostProcessInformation testHostProcessInformation,
CancellationToken cancellation);
}
public interface ITestHostProcessInformation
{
int PID { get; }
int ExitCode { get; }
bool HasExitedGracefully { get; }
}
ITestHostProcessLifetimeHandler ist ein Typ von ITestHostControllersExtension, der als Basis für alle Testhostcontroller-Erweiterungen dient. Wie alle anderen Erweiterungspunkte erbt er ebenfalls von IExtension. Daher können Sie diese Erweiterung wie jede andere mithilfe der IExtension.IsEnabledAsync-API aktivieren oder deaktivieren.
Beachten Sie für diese API die folgenden Details:
BeforeTestHostProcessStartAsync: Diese Methode wird aufgerufen, bevor die Testplattform die Testhosts initiiert.
OnTestHostProcessStartedAsync: Diese Methode wird unmittelbar nach dem Start des Testhosts aufgerufen. Diese Methode bietet ein Objekt, das die ITestHostProcessInformation-Schnittstelle implementiert, die wichtige Details zum Testhostprozessergebnis bereitstellt.
Von Bedeutung
Durch den Aufruf dieser Methode wird die Ausführung des Testhosts nicht angehalten. Wenn Sie die Ausführung anhalten müssen, sollten Sie eine prozessinterne Erweiterung wie ITestApplicationLifecycleCallbacks registrieren und sie mit der prozessexternen Erweiterung synchronisieren.
OnTestHostProcessExitedAsync: Diese Methode wird aufgerufen, wenn die Ausführung der Testsammlung abgeschlossen ist. Sie stellt ein Objekt bereit, das der ITestHostProcessInformation-Schnittstelle entspricht, die wichtige Details zum Ergebnis des Testhostprozesses übermittelt.
Die ITestHostProcessInformation-Schnittstelle stellt die folgenden Details bereit:
-
PID: Die Prozess-ID des Testhosts. -
ExitCode: Der Exitcode des Prozesses. Dieser Wert ist nur in derOnTestHostProcessExitedAsync-Methode verfügbar. Der Versuch, innerhalb derOnTestHostProcessStartedAsync-Methode darauf zuzugreifen, führt zu einer Ausnahme. -
HasExitedGracefully: Ein boolescher Wert, der angibt, ob der Testhost abgestürzt ist. Wenn der Wert „true” ist, wurde der Testhost nicht ordnungsgemäß beendet.
Ausführungsreihenfolge der Erweiterungen
Die Testplattform besteht aus einem Testframework und einer beliebigen Anzahl von Erweiterungen, die prozessintern oder prozessextern ausgeführt werden können. In diesem Dokument wird die Reihenfolge der Aufrufe aller möglichen Erweiterungspunkte beschrieben, um zu verdeutlichen, wann der Aufruf eines Features erwartet wird:
- ITestHostEnvironmentVariableProvider.UpdateAsync: außerhalb des Prozesses
- ITestHostEnvironmentVariableProvider.ValidateTestHostEnvironmentVariablesAsync: Außerhalb des Prozesses
- ITestHostProcessLifetimeHandler.BeforeTestHostProcessStartAsync: Außerhalb des Prozesses
- Start des Testhostprozesses
- ITestHostProcessLifetimeHandler.OnTestHostProcessStartedAsync: Prozessextern; dieses Ereignis kann die Aktionen von prozessinternen Erweiterungen abhängig von Racebedingungen miteinander verknüpfen.
- ITestApplicationLifecycleCallbacks.BeforeRunAsync: Prozessintern
- ITestSessionLifetimeHandler.OnTestSessionStartingAsync: Prozessintern
- ITestFramework.CreateTestSessionAsync: Prozessintern
- ITestFramework.ExecuteRequestAsync: Prozessintern; diese Methode kann einmal oder mehrmals aufgerufen werden. An diesem Punkt übermittelt das Testframework Informationen an den IMessageBus, die vom IDataConsumer genutzt werden können.
- ITestFramework.CloseTestSessionAsync: Prozessintern
- ITestSessionLifetimeHandler.OnTestSessionFinishingAsync: Prozessintern
- ITestApplicationLifecycleCallbacks.AfterRunAsync: Prozessintern
- Die prozessinterne Bereinigung umfasst das Aufrufen von „dispose” und IAsyncCleanableExtension für alle Erweiterungspunkte.
- ITestHostProcessLifetimeHandler.OnTestHostProcessExitedAsync: außerhalb des Prozesses
- Die prozessexterne Bereinigung umfasst das Aufrufen von „dispose” und IAsyncCleanableExtension für alle Erweiterungspunkte.
Erweiterungshelfer
Die Testplattform bietet eine Reihe von Hilfsklassen und -schnittstellen, um die Implementierung von Erweiterungen zu vereinfachen. Diese Hilfsprogramme beschleunigen den Entwicklungsprozess und stellen sicher, dass die Erweiterung den Standards der Plattform entspricht.
Asynchrone Initialisierung und Bereinigung von Erweiterungen
Die Erstellung des Testframeworks und der Erweiterungen über Factorys entspricht dem standardmäßigen .NET-Objekterstellungsmechanismus, bei dem synchrone Konstruktoren verwendet werden. Wenn eine Erweiterung eine intensive Initialisierung erfordert (z. B. mit Zugriff auf das Dateisystem oder Netzwerk), kann sie das async/await-Muster nicht im Konstruktor verwenden, da Konstruktoren „void” zurückgeben, nicht Task.
Daher stellt die Testplattform eine Methode zum Initialisieren einer Erweiterung mit dem async/await-Muster über eine einfache Schnittstelle bereit. Aus Gründen der Symmetrie bietet sie auch eine asynchrone Schnittstelle für die Bereinigung, die Erweiterungen nahtlos implementieren können.
public interface IAsyncInitializableExtension
{
Task InitializeAsync();
}
public interface IAsyncCleanableExtension
{
Task CleanupAsync();
}
IAsyncInitializableExtension.InitializeAsync: Diese Methode wird garantiert nach der Erstellungsfabrik aufgerufen.
IAsyncCleanableExtension.CleanupAsync: Diese Methode wird immer mindestens einmal während der Beendigung der Testsitzung vor dem standardmäßigen DisposeAsync- oder Dispose-Aufruf aufgerufen.
Von Bedeutung
Wie die Dispose-Standardmethode kann CleanupAsync mehrmals aufgerufen werden. Wenn die CleanupAsync-Methode eines Objekts mehrmals aufgerufen wird, muss das Objekt alle Aufrufe nach dem ersten Aufruf ignorieren. Das Objekt darf keine Ausnahme auslösen, wenn seine CleanupAsync-Methode mehrmals aufgerufen wird.
Hinweis
Standardmäßig ruft die Testplattform die DisposeAsync-Methode auf, sofern diese verfügbar ist, oder die Dispose-Methode, wenn diese implementiert ist. Beachten Sie, dass die Testplattform nicht beide dispose-Methoden aufruft, sondern die asynchrone Methode priorisiert, sofern diese implementiert ist.
CompositeExtensionFactory<T>
Wie im Abschnitt zu den Erweiterungen beschrieben, ermöglicht Ihnen die Testplattform das Implementieren von Schnittstellen, um sowohl prozessinterne als auch prozessexterne benutzerdefinierte Erweiterungen zu integrieren.
Jede Schnittstelle stellt ein bestimmtes Feature bereit, und gemäß dem .NET-Entwurf implementieren Sie diese Schnittstelle in einem spezifischen Objekt. Wie in den entsprechenden Abschnitten beschrieben, können Sie die Erweiterung selbst mithilfe der spezifischen Registrierungs-API AddXXX über das TestHost- oder TestHostController-Objekt von ITestApplicationBuilder registrieren.
Wenn Sie den Zustand zwischen zwei Erweiterungen teilen müssen, wird dies erschwert durch die Tatsache, dass Sie unterschiedliche Objekte implementieren und registrieren können, die verschiedene Schnittstellen verwenden. Ohne jegliche Unterstützung würden Sie eine Möglichkeit benötigen, eine Erweiterung an die andere weiterzugeben, um Informationen auszutauschen, was den Entwurf verkompliziert.
Die Testplattform bietet daher eine fortschrittliche Methode, um mehrere Erweiterungspunkte mit demselben Typ zu implementieren, wodurch die Datenfreigabe zu einer einfachen Aufgabe wird. Dazu müssen Sie lediglich die CompositeExtensionFactory<T> verwenden, die dann mit der gleichen API wie eine einzelne Schnittstellenimplementierung registriert werden kann.
Angenommen, Sie haben einen Typ, der sowohl ITestSessionLifetimeHandler als auch IDataConsumer implementiert. Dies ist ein gängiges Szenario, da Sie häufig Informationen vom Testframework sammeln und Ihr Artefakt dann beim Abschluss der Testsitzung mithilfe des IMessageBus innerhalb von ITestSessionLifetimeHandler.OnTestSessionFinishingAsync senden möchten.
Dazu implementieren Sie normalerweise die Schnittstellen:
internal class CustomExtension : ITestSessionLifetimeHandler, IDataConsumer, ...
{
...
}
Nachdem Sie das CompositeExtensionFactory<CustomExtension> für Ihren Typ erstellt haben, können Sie es sowohl bei den IDataConsumer- und ITestSessionLifetimeHandler-APIs registrieren, die eine Überladung für das CompositeExtensionFactory<T> bieten:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
var factory = new CompositeExtensionFactory<CustomExtension>(serviceProvider => new CustomExtension());
builder.TestHost.AddTestSessionLifetimeHandle(factory);
builder.TestHost.AddDataConsumer(factory);
Der Factorykonstruktor verwendet den IServiceProvider, um auf die von der Testplattform bereitgestellten Dienste zuzugreifen.
Die Testplattform ist für die Verwaltung des Lebenszyklus der zusammengesetzten Erweiterung verantwortlich.
Da die Testplattform sowohl prozessinterne als auch prozessexterne Erweiterungen unterstützt, ist es nicht möglich, Erweiterungspunkte beliebig zu kombinieren. Die Erstellung und Verwendung von Erweiterungen ist vom Hosttyp abhängig, d. h. Sie können nur prozessinterne (TestHost) und prozessexterne (TestHostController) Erweiterungen gruppieren.
Folgende Kombinationen sind möglich:
- Für
ITestApplicationBuilder.TestHostkönnen SieIDataConsumerundITestSessionLifetimeHandlerkombinieren. - Für
ITestApplicationBuilder.TestHostControllerskönnen SieITestHostEnvironmentVariableProviderundITestHostProcessLifetimeHandlerkombinieren.