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 MTP über das Testframework selbst hinaus behandelt. Informationen zur Erstellung von Testframeworks finden Sie unter Erstellen eines Testframeworks.
Die vollständige Zusammenfassung des Erweiterungspunkts und der prozesinternen und prozessexternen Konzepte finden Sie unter Erstellen von benutzerdefinierten Erweiterungen.
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.
Tipp
Jede in diesem Artikel aufgeführte Erweiterung enthält ein manuelles Registrierungs-Snippet (z. B. builder.TestHost.AddDataConsumer(...)). Wenn Sie Ihre Erweiterung als NuGet-Paket versenden, können Sie es Verbrauchern ermöglichen, den manuellen Aufruf zu überspringen, indem Sie eine TestingPlatformBuilderHook und eine kleine MSBuild-Props-Datei verfügbar machen. Der automatisch generierte Einstiegspunkt ruft dann Ihren Hook auf. Ausführliche Informationen finden Sie unter "Automatische Registrierung Ihrer Erweiterung bei TestingPlatformBuilderHook" .
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 folgt mit der ITestApplicationBuilder-Schnittstelle über die Registrierungsfactory 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 Bedeutung 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 oder Arität (Anzahl von Argumenten) einer Option ist die Anzahl von Werten, die übergeben werden können, wenn diese Option oder dieser Befehl angegeben ist. Folgende Stelligkeiten sind derzeit verfügbar:
-
Zero: Stellt eine Stelligkeit der Argumente von Null dar. -
ZeroOrOne: Stellt eine Stelligkeit der Argumente von Null oder 1 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 zum Registrieren einer benutzerdefinierten ITestSessionLifetimeHandlerAPI die folgende API:
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(ITestSessionContext testSessionContext);
Task OnTestSessionFinishingAsync(ITestSessionContext testSessionContext);
}
public interface ITestSessionContext
{
SessionUid SessionUid { get; }
CancellationToken CancellationToken { get; }
}
public readonly struct SessionUid(string value)
{
public string Value { get; } = value;
}
public interface ITestHostExtension : IExtension
{
}
Von Bedeutung
In MTP 2.0.0 wurden beide Methoden so geändert, dass sie einen einzelnen ITestSessionContext-Parameter verwenden, der SessionUid und CancellationToken verfügbar macht. In MTP 1.x akzeptierte jede Methode ein separates SessionUid- und ein separates CancellationToken-Argument. Weitere Informationen finden Sie unter Migrieren von Microsoft. Testing.Platform (MTP) v1 zu v2.
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 erhält den ITestSessionContext, dessen SessionUid einen opaken 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.
Schließlich stellt das ITestSessionContext ein CancellationToken bereit, 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
Von Bedeutung
ITestApplicationLifecycleCallbacks wurde in MTP 2.0.0 entfernt. Verwenden Sie stattdessen ITestHostApplicationLifetime. Weitere Informationen finden Sie unter Migrieren von Microsoft. Testing.Platform (MTP) v1 zu v2.
Über die ITestHostApplicationLifetime Schnittstelle kann ein prozessinterner Erweiterungscode am Anfang und Ende des Testhosts ausgeführt werden.
Verwenden Sie zum Registrieren einer benutzerdefinierten ITestHostApplicationLifetimeAPI die folgende API:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddTestHostApplicationLifetime(
static serviceProvider
=> new CustomTestHostApplicationLifetime());
Die Factory verwendet den IServiceProvider , um auf die von der Testplattform angebotenen Dienste zuzugreifen.
Von Bedeutung
Die Registrierungsreihenfolge ist wichtig, da die APIs in der Reihenfolge aufgerufen werden, in der sie registriert wurden.
Die ITestHostApplicationLifetime-Schnittstelle umfasst die folgenden Methoden:
public interface ITestHostApplicationLifetime : ITestHostExtension
{
Task BeforeRunAsync(CancellationToken cancellationToken);
Task AfterRunAsync(
int exitCode,
CancellationToken cancellationToken);
}
public interface ITestHostExtension : IExtension
{
}
Die ITestHostApplicationLifetime-Schnittstelle erweitert ITestHostExtension, die als Basis für alle Erweiterungen für Testhosts 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
Das IDataConsumer ist eine In-Process-Erweiterung, die IData Informationen abonnieren und empfangen kann, die vom Testframework und seinen Erweiterungen im IMessageBus veröffentlicht 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 : IExtension
{
Type[] DataTypesConsumed { get; }
Task ConsumeAsync(
IDataProducer dataProducer,
IData value,
CancellationToken cancellationToken);
}
public interface IData
{
string DisplayName { get; }
string? Description { get; }
}
Von Bedeutung
In MTP 2.0.0 wurde IDataConsumer in den Namespace Microsoft.Testing.Platform.Extensions verschoben und leitet jetzt direkt von IExtension ab. In MTP 1.x wurde ITestHostExtension erweitert. Sie registrieren es weiterhin bei builder.TestHost.AddDataConsumer(...). Weitere Informationen finden Sie unter Migrieren von Microsoft. Testing.Platform (MTP) v1 zu v2.
Die IDataConsumer erbt 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 immer dann ausgelöst, wenn Daten eines Typs, den der aktuelle Abonnent abonniert hat, an IMessageBus veröffentlicht werden. Sie 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 zur Veröffentlichung verschiedener Arten IData impliziert, dass der Verbraucher den Typ selbst einschalten muss, um ihn in den richtigen Typ zu umwandeln und auf die spezifischen Informationen zuzugreifen.
Eine Beispielimplementierung eines Consumers, der die von einem TestNodeUpdateMessage erzeugte 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
Verarbeiten Sie die Nutzlast direkt innerhalb der ConsumeAsync Methode. Ein reguläres IDataConsumer verarbeitet Daten asynchron: Der IMessageBus reiht jede veröffentlichte Nutzlast in die Warteschlange ein und verarbeitet sie in einer Hintergrundschleife, sodass IMessageBus.PublishAsync den Produzenten nicht blockiert, und es gibt keine Garantie dafür, wann ConsumeAsync ausgeführt wird, während der Produzent seine Arbeit fortsetzt. Die Plattform serialisiert die Übermittlung, sodass pro Consumer jeweils nur eine Nutzlast verarbeitet wird, wodurch die Notwendigkeit einer komplexen Synchronisierung innerhalb eines einzelnen Verbrauchers beseitigt wird.
Hinweis
Für Szenarien, die die Garantie erfordern, dass die Verarbeitung erfolgt, bevor der Produzent fortfährt (z. B. bevor ein Test zu laufen beginnt), hat MTP 2.3.0 die experimentelle IBlockingDataConsumer Marker-Schnittstelle eingeführt (setzt voraus, dass die TPEXP Diagnose unterdrückt wird). Ein Consumer, der auch IBlockingDataConsumer implementiert, wird vom Nachrichtenbus inline aufgerufen: Aufrufe werden serialisiert, PublishAsync blockiert, bis ConsumeAsync abgeschlossen ist, und jede von ConsumeAsync ausgelöste Ausnahme wird an den Produzenten zurückpropagiert, der die Daten veröffentlicht hat. Der Message-Bus stellt die Daten eines Produzenten nicht an eben diesen Produzenten zurück (dieselbe UID), sodass das Veröffentlichen unter der eigenen UID sicher ist. Ein blockierender Verbraucher darf jedoch aus ConsumeAsync heraus keine Daten publizieren, die unter einer anderen Produzenten-UID an sich selbst zurückgeleitet werden, da diese Reentranz zu einem Deadlock führen würde.
Warnung
Bei Verwendung IDataConsumer in Verbindung mit ITestSessionLifetimeHandler innerhalb eines zusammengesetzten Erweiterungspunktsist es wichtig, alle nach der Ausführung von ITestSessionLifetimeHandler.OnTestSessionFinishingAsync empfangenen Daten zu ignorieren. 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.
Nachrichtenbusdateiartefakte
Eine benutzerdefinierte IData Payload gibt weder in der Benutzeroberfläche noch in der Befehlszeile automatisch etwas aus. Die Plattform zeigt nur Daten an, für die ein passender Verbraucher registriert ist. Wenn Sie also Ihren eigenen IData Typ veröffentlichen und nichts verbraucht, wird nichts gedruckt oder weitergeleitet. Damit Dateien, die von Ihrer Erweiterung erzeugt werden, für Benutzer und Tools sichtbar sind, veröffentlichen Sie eine der integrierten Meldungen zu Dateiartefakten, die die integrierten terminal- und dotnet test-Consumer bereits erkennen.
Veröffentlichen Sie für Dateien auf Laufebene oder Sitzungsebene eine FileArtifact oder eine SessionFileArtifact. Beide wurden in MTP 1.0.0 eingeführt und befinden sich im Namespace Microsoft.Testing.Platform.Extensions.Messages:
-
FileArtifactist ohne Bereich. Verwenden Sie sie für eine Datei, die nicht an eine bestimmte Testsitzung gebunden ist. -
SessionFileArtifactist durch seinSessionUidauf einen Lauf/eine Sitzung beschränkt. Verwenden Sie sie für Artefakte, die als Ganzes erstellt wurden, z. B. Abdeckungsergebnisse, Berichte, Dumps oder aufgezeichnete Videos.
Das integrierte Terminal und die dotnet test Consumer erfassen beide Typen und geben die Dateipfade aus oder leiten sie weiter, sodass die Dateien in der Konsolenausgabe und über die dotnet test Pipe sichtbar werden. Da Verbraucher die ultimative Präsentation steuern, halten Sie jede Datei auf dem Datenträger und verfügbar, solange sie verbraucht oder weitergeleitet werden kann; löschen Sie sie nicht innerhalb desselben PublishAsync Anrufs.
Das Artefaktobjekt selbst trägt keine Produzentenidentität. Der Nachrichtenbus übergibt die ursprüngliche IDataProducer über das dataProducer-Argument von IDataConsumer.ConsumeAsync an jeden Consumer, sodass Consumer aus diesem Argument erfahren, wer eine Datei erzeugt hat, und nicht aus dem Artefakt. Der Nachrichtenbus übernimmt auch nicht die Verantwortung für die referenzierte Datei, verschiebt sie nicht und löscht sie nicht: Der Produzent ist für die Lebensdauer der Datei verantwortlich, und Konsumenten erhalten, lesen oder leiten lediglich ihren Pfad weiter.
Hinweis
Die Registrierung von Drittanbietern IDataConsumer ist nur auf builder.TestHost (dem In-Process-Testhost ) öffentlich. Es gibt keine öffentliche builder.TestHostControllers.AddDataConsumer API für Consumer außerhalb des Prozesses. Die Report-Erweiterungen des Erstanbieters, die Artefakte anzeigen, nutzen eine interne Plattformintegration, auf die sich benutzerdefinierte Erweiterungen nicht verlassen können. Wenn Sie Dateien aus Ihrer eigenen Erweiterung anzeigen möchten, veröffentlichen Sie die hier beschriebenen integrierten Artefaktmeldungen, und lassen Sie die integrierten Verbraucher diese präsentieren.
Ein Produzent, der ein Sitzungsartefakt veröffentlicht, muss IDataProducer implementieren und in DataTypesProduced die exakten Laufzeitnachrichtentypen auflisten, die er veröffentlicht. Im folgenden Beispiel wird ein Abdeckungsbericht veröffentlicht, wenn die Sitzung abgeschlossen ist:
internal sealed class CoverageReportProducer(IMessageBus messageBus)
: IDataProducer, ITestSessionLifetimeHandler
{
public string Uid => nameof(CoverageReportProducer);
public string Version => "1.0.0";
public string DisplayName => "Coverage report producer";
public string Description => "Publishes the coverage report as a session artifact.";
// List the exact runtime message types this producer publishes.
public Type[] DataTypesProduced => new[] { typeof(SessionFileArtifact) };
public Task<bool> IsEnabledAsync() => Task.FromResult(true);
public Task OnTestSessionStartingAsync(ITestSessionContext context)
=> Task.CompletedTask;
public Task OnTestSessionFinishingAsync(ITestSessionContext context)
{
var report = new FileInfo("coverage.cobertura.xml");
return messageBus.PublishAsync(
this,
new SessionFileArtifact(
context.SessionUid,
report,
"Code coverage",
"Cobertura coverage report for the run."));
}
}
Wenn Sie eine Datei an einen bestimmten Test anfügen möchten, dotnet testdamit das Terminal und IDEs diesen Test zuordnen und anzeigen können, veröffentlichen Sie kein eigenständiges Dateiartefakt. Fügen Sie stattdessen dem TestNode einen oder mehrere FileArtifactProperty-Einträge hinzu, das Ihr Testframework über ein TestNodeUpdateMessage meldet.
FileArtifactProperty wurde in MTP 1.7.0 eingeführt:
var testNode = new TestNode
{
Uid = testUid,
DisplayName = testDisplayName,
Properties = new PropertyBag(
PassedTestNodeStateProperty.CachedInstance,
new FileArtifactProperty(
new FileInfo("screenshot.png"),
"Failure screenshot",
"Screenshot captured while the test ran.")),
};
await messageBus.PublishAsync(
dataProducer,
new TestNodeUpdateMessage(sessionUid, testNode));
Von Bedeutung
TestNodeFileArtifact ist veraltet und wurde in MTP 2.0.0 entfernt. Verwenden Sie zum Anhängen von Dateien auf Testebene FileArtifactProperty auf der TestNode. Weitere Informationen finden Sie unter Migrieren von Microsoft. Testing.Platform (MTP) v1 zu v2.
Hinweis
MTP 2.4.0 (Stand Juli 2026 unveröffentlicht) fügt FileArtifact und SessionFileArtifact eine experimentelle kind-Konstruktorüberladung und Kind-Eigenschaft hinzu (wofür die TPEXP-Diagnose unterdrückt werden muss).
Kind ist ein vom Hersteller bestätigter Reverse-DNS-Bezeichner des Artefaktformats (z. B. microsoft.testing.trx, microsoft.testing.junit, microsoft.testing.ctrf oder microsoft.testing.html), der bei der Nachbearbeitung verwendet werden kann, um Artefakte desselben Formats zur Konsolidierung zu gruppieren. Lassen Sie es null leer, oder lassen Sie es weg, wenn der Hersteller keinen bekannten Typ angibt. Derzeit ist Kind nur ein Metadatenvertrag; gehen Sie nicht davon aus, dass darüber hinaus eine umfassende Merge-Orchestrierung erfolgt.
#pragma warning disable TPEXP // Experimental API.
new SessionFileArtifact(
context.SessionUid,
trxFile,
"TRX report",
"Test results in TRX format.",
kind: "microsoft.testing.trx");
#pragma warning restore TPEXP
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 ITestHostApplicationLifetime 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.
Registrieren Sie Ihre Erweiterung automatisch mit TestingPlatformBuilderHook
Jeder vorherige Erweiterungsabschnitt zeigt einen manuellen Registrierungsanruf (z. B builder.TestHost.AddDataConsumer(...). ). Die Aufforderung der Verbraucher, ihre Main Methode zu bearbeiten, ist eine schlechte Onboarding-Erfahrung. Das Microsoft.Testing.Platform.MSBuild-Paket löst dies durch Generieren einer SelfRegisteredExtensions.AddSelfRegisteredExtensions(builder, args)-Methode, die vom automatisch generierten Einstiegspunkt ausgeführt wird. Um Die Erweiterung an diese generierte Methode zu anschließen, versenden Sie zwei Artefakte in Ihrem NuGet-Paket:
- Eine öffentliche statische
TestingPlatformBuilderHookKlasse mit einerAddExtensionsMethode, die Die Erweiterung registriert. - Eine MSBuild-Props-Datei, die ein
<TestingPlatformBuilderHook>Element deklariert, das auf diese Klasse verweist.
Wenn ein Benutzer Ihr Paket installiert, greift die MSBuild-Integration das Element auf und generiert den Aufruf Ihres Hooks, und Ihre Erweiterung wird ohne Codeänderungen auf Benutzerseite registriert.
Hinweis
Die automatische Registrierung funktioniert nur, wenn im Projekt des Nutzers Microsoft.Testing.Platform.MSBuild vorhanden ist (es ist transitiv über MSTest-, NUnit- und xUnit-Runner enthalten) und er sich nicht durch das Festlegen von <GenerateTestingPlatformEntryPoint>false</GenerateTestingPlatformEntryPoint> dagegen entschieden hat. Verbraucher, die den automatisch generierten Einstiegspunkt deaktivieren, müssen ihre manuelle Registrierungs-API weiterhin über ihre Main Methode aufrufen.
Erstellen Sie die Hook-Klasse
Fügen Sie Ihrer Erweiterungsassembly ein public static class TestingPlatformBuilderHook mit einer AddExtensions(ITestApplicationBuilder, string[])-Methode hinzu, die dieselbe Registrierung ausführt, die Benutzer andernfalls manuell aufrufen würden:
using Microsoft.Testing.Platform.Builder;
namespace Contoso.MyExtension;
public static class TestingPlatformBuilderHook
{
public static void AddExtensions(ITestApplicationBuilder testApplicationBuilder, string[] arguments)
=> testApplicationBuilder.AddMyExtension();
}
Der Klassenname muss nicht TestingPlatformBuilderHook sein – das MSBuild-Element verweist darauf mit dem vollständigen Typnamen, wobei der Code jedoch mit den in-Box-Erweiterungen wie Microsoft.Testing.Extensions.Retry und Microsoft.Testing.Extensions.HotReload konsistent bleibt.
Die Methode muss:
- Sei
public static. - Weisen Sie einen ersten Parameter vom Typ
Microsoft.Testing.Platform.Builder.ITestApplicationBuilderauf. - Weisen Sie einen zweiten Typparameter
string[]auf (die Befehlszeilenargumente, die an den Testhost übergeben werden). Sie können sie ignorieren, wenn Ihre Erweiterung sie nicht benötigt. - Geben Sie
voidzurück.
Deklarieren des MSBuild-Elements
Stellen Sie in Ihrem NuGet-Paket unter buildMultiTargeting/<PackageId>.props eine Props-Datei bereit. Deklarieren Sie einen <TestingPlatformBuilderHook>-Eintrag, der die MSBuild-Aufgabe auf Ihre Hook-Klasse verweist:
<Project>
<ItemGroup>
<TestingPlatformBuilderHook Include="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">
<DisplayName>Contoso.MyExtension</DisplayName>
<TypeFullName>Contoso.MyExtension.TestingPlatformBuilderHook</TypeFullName>
</TestingPlatformBuilderHook>
</ItemGroup>
</Project>
Die Metadaten sind wie folgt:
-
Include: Eine GUID, die Ihren Hook eindeutig identifiziert. Siehe GUIDIncludeist ein zufälliger Bezeichner. -
DisplayName: Der Anzeigename, der in MSBuild-Diagnosemeldungen angezeigt wird, wenn der Einstiegspunkt generiert wird. Verwenden Sie Ihren Paket- oder Erweiterungsnamen. -
TypeFullName: Der vollqualifizierte Name der KlasseTestingPlatformBuilderHook, die Sie zuvor erstellt haben. Die MSBuild-Aufgabe verwendet dies, umglobal::Contoso.MyExtension.TestingPlatformBuilderHook.AddExtensions(builder, args);in den generierten Einstiegspunkt einzufügen.
Die Include GUID ist ein zufälliger Bezeichner.
Die GUID im Attribut Include ist nicht dieselbe wie IExtension.Uid Ihrer Erweiterung. Es handelt sich um einen Registrierungsbezeichner, der von der MSBuild-Aufgabe verwendet wird, um Hooks über NuGet-Verweise zu deduplizieren und (in einigen bekannten Fällen) zu ordnen.
Wenn Sie eine neue Erweiterung erstellen, generieren Sie eine völlig neue GUID und codieren Sie sie fest in Ihrer props-Datei ein. Einige Möglichkeiten, einen zu erstellen:
- Powershell:
[guid]::NewGuid() - Visual Studio: Tools>GUID erstellen
-
uuidgenunter Linux und macOS
Von Bedeutung
Kopieren Sie niemals eine GUID aus der Props-Datei einer anderen Erweiterung (ob von Microsoft oder von einem Drittanbieter geliefert). Zwei Erweiterungen, die denselben Wert für Include haben, werden als Duplikate behandelt: nur ein Hook wird aufgerufen, sodass Ihre Erweiterung stillschweigend nicht registriert wird.
Hinweis
Sobald Sie eine GUID veröffentlicht haben, behandeln Sie sie als unveränderlich. Das Ändern in einer späteren Version ist selbst harmlos, aber das erneute Verwenden des alten Werts für einen anderen Hook in einer zukünftigen Paketversion kann Verbraucher verwirren, die beide Versionen in ihrem Abhängigkeitsdiagramm während eines Upgrades haben.
Überprüfen, ob der Haken verkabelt ist
Nachdem Sie das Paket in einem Testprojekt installiert haben, das Microsoft.Testing.Platform.MSBuild verwendet, erstellen Sie das Projekt, und prüfen Sie die generierte datei SelfRegisteredExtensions.g.cs unter obj/<Configuration>/<TargetFramework>/. Sie sollten einen Aufruf an Ihrem Hook sehen, z. B.:
public static void AddSelfRegisteredExtensions(this global::Microsoft.Testing.Platform.Builder.ITestApplicationBuilder builder, string[] args)
{
global::Contoso.MyExtension.TestingPlatformBuilderHook.AddExtensions(builder, args);
}
Wenn der Aufruf fehlt, überprüfen Sie, ob die Props-Datei unter buildMultiTargeting/ (nicht build/) innerhalb von .nupkg enthalten ist, ob DisplayName- und TypeFullName-Metadaten vorhanden sind und ob der Verbraucher nicht <GenerateTestingPlatformEntryPoint>false</GenerateTestingPlatformEntryPoint> gesetzt hat.
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.
- ITestHostApplicationLifetime.BeforeRunAsync: im Prozess
- 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
- ITestHostApplicationLifetime.AfterRunAsync: Im Prozess
- 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 erweiterungen durch Fabriken entspricht dem Standard-.NET Objekterstellungsmechanismus, der synchrone Konstruktoren verwendet. 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 immer nach der Erstellungsfactory 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 behebt ein bestimmtes Feature, und gemäß .NET Design implementieren Sie diese Schnittstelle in einem bestimmten 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.
Hinweis
IDataConsumer ist eine In-Process-Erweiterung , sodass benutzerdefinierte Verbraucher nur über builder.TestHost (einschließlich via CompositeExtensionFactory<T>) registriert werden. Es gibt keine öffentliche API, um ein IDataConsumer bei builder.TestHostControllers zu registrieren.