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.
Der MTP-Servermodus ermöglicht es einem Editor, einer IDE, einer benutzerdefinierten Testoberfläche oder einem anderen Testtool, eine Testanwendung zu starten und die Testerkennung und -ausführung über JSON-RPC zu steuern. Verwenden Sie den Servermodus, wenn Ihr Tool Testmetadaten, die Ausführung ausgewählter Tests, Fortschrittsaktualisierungen oder Ergebnisartefakte benötigt, ohne die Terminalausgabe analysieren zu müssen. Führen Sie für reguläre Befehlszeilen- und CI-Läufe die Testanwendung direkt aus oder verwenden Sie dotnet test.
Für die einmalige Erkennung ohne dauerhafte Verbindung verwenden Sie --list-tests json. Für einen bestehenden VSTest-Client siehe Programmgesteuerte Testerkennung und -ausführung.
Starten Sie den öffentlichen JSON-RPC-Server mit --server oder --server jsonrpc.
Important
Das .NET SDK verwendet ein separates internes binärprotokoll über --server dotnettestcli. Übergeben Sie diesen Wert nicht selbst.
dotnet test konfiguriert die Transport- und Protokollargumente.
Nur den Quellclient verwenden
MTP 2.4 bietet einen kanonischen JSON-RPC Client. Fügen Sie das Paket Microsoft.Testing.Platform.ServerMode.Client.Sources hinzu. Das Paket fügt die C#-Quelle in Ihr Projekt ein, anstatt eine Laufzeitassembly hinzuzufügen.
Das Source-only-Design bietet Folgendes:
- Keine Laufzeit-DLL oder zusätzliche Bereitstellungsabhängigkeit.
- Native AOT-kompatible, spiegellose Serialisierung.
- Protokolltypen und Serialisierungscode aus demselben Repository wie der MTP-Server.
Die injizierten MtpServerClient, IMtpServerClient und Protokolltypen können eine Verbindung initialisieren, Tests ermitteln, Tests ausführen, das Beenden des Servers anfordern und Aktualisierungen von Testknoten melden. Alle injizierten Typen bleiben internal für die verbrauchende Assembly. Der Client verarbeitet Prozessstart, Transport, Nachrichtenrahmen und Serialisierung; Sie müssen JSON-RPC nicht selbst implementieren.
Das Paket kompiliert die C#-Quelle. Wenn Sie den Client in Visual Basic oder einer anderen .NET-Sprache verwenden möchten, fügen Sie einen Verweis auf eine C#-Klassenbibliothek hinzu, die eine eigene öffentliche API um die injizierten Typen herum verfügbar macht, wie im folgenden Beispiel.
Tests aus .NET entdecken und ausführen
In diesem Beispiel wird die nur Quellcode enthaltende Clientpaketversion 2.5.2 verwendet und auf .NET 10 ausgerichtet. Erstellen Sie zuerst eine MTP-fähige Testanwendung, und übergeben Sie dann den Pfad zu der erstellten .dll bzw. ausführbaren Datei an den Client.
Der freigegebene C#-Hilfsprogramm ermittelt Tests, druckt die serialisierbaren Metadaten jedes Knotens und führt die ermittelten Tests nach UID aus. Um eine Teilmenge auszuführen, wählen Sie die erforderlichen UIDs aus den Discoveryupdates aus, und übergeben Sie nur diese UIDs an RunTestsAsync. Verwenden Sie keine Anzeigenamen als Testidentitäten.
using System.Text.Json;
using Microsoft.Testing.Platform.ServerMode.Client;
namespace TestClientSample;
public static class TestClient
{
public static async Task<int> DiscoverAndRunAsync(
string testApplicationPath,
CancellationToken cancellationToken = default)
{
string path = Path.GetFullPath(testApplicationPath);
var testUids = new HashSet<string>(StringComparer.Ordinal);
using (IMtpServerClient client = await MtpServerClient.LaunchAsync(
path, cancellationToken: cancellationToken))
{
try
{
MtpServerCapabilities capabilities =
await client.InitializeAsync(cancellationToken);
if (capabilities.MultiConnectionProvider)
{
throw new NotSupportedException("A passive controller connection can't discover or run tests.");
}
if (!capabilities.SupportsDiscovery)
{
throw new NotSupportedException("The test application doesn't support discovery.");
}
client.LogReceived += (_, log) => Console.Error.WriteLine(log.Message);
client.TestNodesUpdated += (_, update) =>
{
foreach (MtpTestNodeUpdate change in update.Changes)
{
Console.WriteLine(JsonSerializer.Serialize(change.Node));
if (change.NodeType != "group"
&& change.ExecutionState == "discovered"
&& change.Uid is string uid)
{
testUids.Add(uid);
}
}
};
await client.DiscoverTestsAsync(cancellationToken);
await client.ExitAsync(cancellationToken);
}
finally
{
await client.ShutdownAsync();
}
}
if (testUids.Count == 0)
{
Console.Error.WriteLine("No tests were discovered.");
return 8;
}
var completedUids = new HashSet<string>(StringComparer.Ordinal);
bool hasUnsuccessfulResult = false;
using IMtpServerClient runClient = await MtpServerClient.LaunchAsync(
path, cancellationToken: cancellationToken);
try
{
MtpServerCapabilities capabilities =
await runClient.InitializeAsync(cancellationToken);
if (capabilities.MultiConnectionProvider)
{
throw new NotSupportedException("A passive controller connection can't discover or run tests.");
}
runClient.LogReceived += (_, log) => Console.Error.WriteLine(log.Message);
runClient.TestNodesUpdated += (_, update) =>
{
foreach (MtpTestNodeUpdate change in update.Changes)
{
Console.WriteLine(JsonSerializer.Serialize(change.Node));
if (change.Uid is string uid
&& testUids.Contains(uid)
&& change.ExecutionState is string state
&& state is not ("discovered" or "in-progress")
&& (!change.Node.TryGetValue("retry.is-superseded", out object? superseded)
|| superseded is not true))
{
completedUids.Add(uid);
hasUnsuccessfulResult |= state is not ("passed" or "skipped");
}
}
};
MtpRunResult result = await runClient.RunTestsAsync(testUids, cancellationToken);
foreach (MtpAttachment artifact in result.Artifacts)
{
Console.WriteLine($"Artifact: {artifact.Uri}");
}
await runClient.ExitAsync(cancellationToken);
if (hasUnsuccessfulResult || !testUids.All(completedUids.Contains))
{
Console.Error.WriteLine("A selected test failed or didn't report a final result.");
return 2;
}
return 0;
}
finally
{
await runClient.ShutdownAsync();
}
}
}
Das Hilfsprogramm startet einen separaten Testprozess für die Erkennung und Ausführung, sodass MtpServerCapabilities.MultiRequestSupport nicht erforderlich ist. Um einen Prozess wiederzuverwenden, prüfen Sie nach InitializeAsync, ob dies möglich ist, bevor Sie mehrere Anfragen senden. Überprüfen Sie auch SupportsDiscovery, bevor Sie die Erkennung anfordern.
Important
Die Starthilfen des Clients nur für Quellen verwalten eine JSON-RPC-Verbindung. In Version 2.5.2 akzeptieren sie die zusätzlichen Verbindungen, die von controllergestützten Erweiterungen verwendet werden, nicht und leiten sie nicht weiter. Beispielsweise kann die TRX-Berichterstellung auf Windows einen Controller starten, dessen passive Verbindung vor dem aktiven Testhost eingeht. Eine Verbindung mit MultiConnectionProvider, das auf true gesetzt ist, sendet nur Updates und Artefakte; senden Sie keine Ermittlungs- oder Ausführungsanforderungen an sie. Das Beispiel lehnt diese Verbindung ab.
Führen Sie für Controller-gesicherte Berichte die Testanwendung über die CLI oder dotnet test aus, oder verwenden Sie einen Toolclient, der die zusätzlichen Verbindungen des Protokolls verarbeitet. Ein Client mit einer einzigen Verbindung kann weiterhin Artefakte empfangen, die der aktive Testhost veröffentlicht.
Die C#- und Visual Basic Konsolenprojekte verweisen beide auf das C#-Hilfsprojekt:
using TestClientSample;
if (args.Length != 1)
{
Console.Error.WriteLine("Usage: DiscoverAndRun <test-application-path>");
return 1;
}
return await TestClient.DiscoverAndRunAsync(args[0]);
Führen Sie eines der beiden Konsolenprojekte mit dem Pfad der Testanwendung als Argument aus:
dotnet run --project <client-project> -- <test-application-path>
Das Beispiel gibt zurück 0 , wenn jede ausgewählte UID ein endgültiges Ergebnis und alle nicht abgelösten Endergebnisberichte passed oder skipped, 2 für erfolglose oder fehlende endgültige Ergebnisse, und 8 wenn die Ermittlung keine Tests zurückgibt. Diese Entscheidungen gehören zum Musterclient. Eine abgeschlossene RunTestsAsync Aufgabe bedeutet, dass die Protokollanforderung abgeschlossen wurde und nicht, dass die Tests bestanden wurden.
MtpRunResult.Artifacts enthält Ausführungsartefakte, keine Pass-/Fail-Bewertung. Verwenden Sie Aktualisierungen des Testknotens, um Testergebnisse zu ermitteln.
Mehrere Ergebnisse können eine UID gemeinsam nutzen. Im Beispiel wird ein früherer Wiederholungsversuch nur dann ignoriert, wenn retry.is-supersededtrue ist; die UID-Gleichheit allein begründet noch keinen Wiederholungsversuch. Ein später übergebenes oder übersprungenes Ergebnis kann einen früheren, nicht ersetzten Fehler nicht löschen.
Updates und Metadaten nutzen
Abonnieren Sie TestNodesUpdated vor der Erkennung oder Ausführung. Lesen Sie Changes aus jedem Update, und identifizieren Sie Knoten nach Uid. Der Client bietet Komforteigenschaften wie DisplayName, , ExecutionState, ErrorMessageund DurationInMilliseconds. Lesen Sie das Node Wörterbuch für andere serialisierte Felder, einschließlich traits und frameworkspezifische Eigenschaften.
MSTest stellt Testkategorien und benutzerdefinierte TestProperty-Werte als Traits bereit. Die Verfügbarkeit von Metadaten hängt vom Testframework und davon ab, was es während der Erkennung veröffentlicht. Beliebige Attribute und In-Memory-Implementierungen IProperty werden nicht automatisch über Prozessgrenzen hinweg übertragen. Verwenden Sie den öffentlichen TestMetadataProperty Typ, um portable Schlüsselwertmetadaten zu veröffentlichen. Die Plattform- und Kompatibilitätsebenen können andere serialisierbare Felder bereitstellen; ihre internen Eigenschaftentypen sind keine Erweiterungs-APIs. Ausführliche Informationen finden Sie unter Testknoteneigenschaften.
Erwarten Sie nicht, dass das JSON-RPC Node-Wörterbuch dem --list-tests json Dokument entspricht. Die Transporte verwenden unterschiedliche Feldlayouts. Gehen Sie auch nicht davon aus, dass jedes Framework die VSTest-spezifischen vstest.* Felder ausgibt.
Der Client verarbeitet Updates in der Reihenfolge und ruft die entsprechenden Handler auf, bevor die Ermittlung oder Ausführung der Aufgabe abgeschlossen ist. Sie müssen nicht auf das endgültige Update warten. Handler werden synchron auf der Leseschleife des Clients ausgeführt, sodass Sie sie nicht blockieren oder nicht synchron auf den Abschluss eines anderen Clientvorgangs warten. Verlagern Sie aufwendige Arbeit oder UI-Aktualisierungen in den entsprechenden Thread.
Übergeben Sie ein Abbruchtoken an Discovery- oder Ausführungsmethoden, um den Abbruch über das Protokoll anzufordern. Wenn das Testframework Strukturfilter unterstützt, verwenden Sie DiscoverTestsWithFilterAsync oder RunTestsWithFilterAsync mit einem MTP-Strukturfilterausdruck, um Tests auszuwählen, ohne zuerst eine UID-Auflistung zu erstellen. Diese Methoden akzeptieren keine VSTest expressions --filter .
Erfüllen der Clientanforderungen
Verwenden Sie C# 12 oder höher. Das Paket berücksichtigt die Sprachversionseinstellung Ihres Projekts. Wenn Ihr Zielframework standardmäßig auf eine frühere C#-Version festgelegt ist, legen Sie 12.0 auf LangVersion oder höher fest. Das Paket stellt außerdem interne Kompatibilitätstypen für .NET Framework 4.6.2 und netstandard2.0.
Entfernen Sie alle handschriftlichen Kopien des MTP-Clients, bevor Sie das Paket hinzufügen, um doppelte interne Typen zu vermeiden.
Auswählen eines Startmodells
Um die Testanwendung als externen Prozess zu starten, verwenden Sie MtpServerClient.LaunchAsync. Der Client ist für diesen untergeordneten Prozess verantwortlich und beendet ihn bei der Entfernung, wenn nötig.
Um eine externe Testanwendung für eine bestimmte Umgebung zu konfigurieren, legen Sie MtpServerClientOptions.EnvironmentVariables vor dem Start fest. Der Kindprozess erbt die Umgebung des Elternprozesses, wobei die angegebenen Werte auf den Kindprozess angewendet werden. Diese Optionen ändern die Umgebung für einen In-Process-Host nicht.
Jeder Client steuert eine Testanwendung. Erstellen Sie für mehrere Testanwendungen einen Client für jede Anwendung, und aggregieren Sie deren Ergebnisse in Ihrem Tool.
Für eine eingebettete Host- oder UI-Thread-Umgebung, die MtpServerClient.LaunchInProcessAsync nicht verwenden kann, verwenden Sie Process.Start. Die Methode führt die Testanwendung mittels eines asynchronen Callbacks im aktuellen Prozess aus. Blockieren Sie nicht den Thread, der den Startvorgang ausführt, da sich Callback und Client denselben Prozess teilen.
Beide Startmodelle verwenden einen Loopback-TCP-Transport. Browser WebAssembly kann den erforderlichen Listener nicht erstellen, daher löst der In-Process-Start PlatformNotSupportedException aus. Der In-Process-Startvorgang stellt keine alternative WebAssembly-Transportmethode bereit.
Client herunterfahren
Rufen Sie IMtpServerClient.ShutdownAsync() auf, um den Client und die gestartete Anwendung zu beenden, ohne den aufrufenden Thread zu blockieren. Die asynchrone Methode vermeidet die Reaktionsfähigkeit von UI-Thread und Deadlock-Probleme in eingebetteten Hosts.
Bei einem In-Process-Host steuert MtpServerClientOptions.ServerShutdownTimeout, wie lange der Abbau wartet, nachdem der Client die Transportverbindung geschlossen hat. Der Standardwert ist 30 Sekunden. Lesen Sie ServerExitCode nach dem Beenden, um den Exit-Code des Callbacks zu erhalten. Der Wert verbleibt null , während die Anwendung ausgeführt wird oder wenn die Anwendung fehlschlägt, anstatt einen Ausgangscode zurückzugeben.
Clientzustandsbehaftung deklarieren
Legen Sie ab MTP 2.4.1 die nullfähige MtpServerClientOptions.IsStateful Option fest, um zu beschreiben, wie der Client Testknotenupdates verwendet:
| Value | Erklärung |
|---|---|
true |
Der Client behält einen adressierbaren Satz von Testknoten für die Sitzung bei und behält den letzten bekannten Zustand jedes Knotens bei, bis ein anderes Update ihn ändert, wie es ein IDE-Test-Explorer tun könnte. |
false |
Der Client verwendet Updates, ohne diesen Testknotenstatus beizubehalten. |
null (Standardwert) |
Der Client deklariert die Funktion nicht. Der Initialisierungs-Handshake lässt das Feld weg. |
Der Client sendet einen expliziten booleschen Wert über die experimentelle Protokollfunktion capabilities.testing.isStateful. Zustandsbehaftetheit ist unabhängig von der Verbindungslebensdauer und der MultiRequestSupport-Fähigkeit des Servers. Ein Client wird nicht allein dadurch zustandsbehaftet, dass er mehrere Anforderungen über dieselbe Verbindung sendet.
MTP 2.4.0 verwendet eine nicht nullwerte boolesche Option mit einem Standardwert von false. Verwenden Sie MTP 2.4.1 oder höher, um eine nicht deklarierte Funktion beizubehalten.
Verwenden Sie innerhalb eines Frameworks oder einer Erweiterung den experimentellen IClientInfo Dienst , um die Deklaration zu prüfen. Lesen Sie clientInfo.Capabilities.IsStateful für ein konservatives boolesches Ergebnis, das false ist, wenn die Fähigkeit nicht deklariert ist. In MTP 2.4.1 oder höher bewahrt ClientCapabilitiesExtensions.GetIsStateful() den Unterschied zwischen nicht deklarierten, explizit zustandslosen und zustandsbehafteten Clients.
Aushandeln der Protokollkompatibilität
Ab MTP 2.4 ist die Servermodusprotokollaushandlung von der MTP NuGet-Paketversion unabhängig. Ein Client deklariert die von ihr unterstützten Protokollversionen, und der Server wählt eine allgemeine Version aus. Wenn keine unterstützte Version überlappt, wird die Testanwendung mit dem ProtocolError Ausgangscode (12) beendet, anstatt mit einem inkompatiblen Drahtvertrag fortzufahren.
Verwenden Sie das versionsbasierte Servermodus 1.0-JSON-Schema , um Nachrichten zu überprüfen. Die Serialisierer "System.Text.Json" und "Jsonite" folgen demselben Schema und einem Fehlerumschlag.