Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
El modo de servidor MTP permite a un editor, IDE, interfaz de usuario de prueba personalizada u otra herramienta de prueba iniciar una aplicación de prueba y controlar el descubrimiento y la ejecución a través de JSON-RPC. Utilice el modo de servidor cuando su herramienta necesite metadatos de prueba, ejecución de las pruebas seleccionadas, actualizaciones del progreso o artefactos de los resultados sin analizar la salida del terminal. En el caso de las ejecuciones normales de línea de comandos y CI, ejecute la aplicación de prueba directamente o use dotnet test.
Para un descubrimiento puntual sin una conexión persistente, use --list-tests json. Para un cliente VSTest existente, consulte Detección y ejecución de pruebas mediante programación.
Inicie el servidor JSON-RPC público con --server o --server jsonrpc.
Importante
El SDK de .NET utiliza un protocolo binario interno separado a través de --server dotnettestcli. No pases ese valor tú mismo.
dotnet test configura los argumentos de transporte y protocolo.
Utiliza el cliente solo fuente.
MTP 2.4 proporciona un cliente JSON-RPC canónico. Agregue el paquete Microsoft.Testing.Platform.ServerMode.Client.Sources. El paquete inyecta código fuente de C# en su proyecto en lugar de añadir un ensamblado en tiempo de ejecución.
El diseño basado únicamente en el origen proporciona:
- No hay ningún archivo DLL en tiempo de ejecución ni dependencia de implementación adicional.
- Serialización nativa compatible con AOT y sin reflexión.
- Tipos de protocolo y código de serialización del mismo repositorio que el servidor MTP.
Los tipos inyectados MtpServerClient, IMtpServerClient y de protocolo pueden inicializar una conexión, descubrir pruebas, ejecutar pruebas, solicitar el cierre del servidor e informar de actualizaciones de nodos de prueba. Todos los tipos inyectados siguen siendo internal para el ensamblado consumidor. El cliente controla el inicio del proceso, el transporte, el marco de mensajes y la serialización; no es necesario implementar JSON-RPC usted mismo.
El paquete compila el código fuente de C#. Para usar el cliente desde Visual Basic u otro lenguaje .NET, haga referencia a una biblioteca de clases de C# que expone su propia API pública en torno a los tipos insertados, como en el ejemplo siguiente.
Descubrir y ejecutar pruebas desde .NET
En este ejemplo se usa la versión 2.5.2 del paquete cliente solo de código fuente y está dirigido a .NET 10. Compile primero una aplicación de prueba habilitada para MTP y, a continuación, pase la ruta de acceso a su .dll compilado o ejecutable al cliente.
El asistente de C# compartido detecta pruebas, imprime los metadatos serializables de cada nodo y ejecuta las pruebas detectadas por UID. Para ejecutar un subconjunto, seleccione los UID necesarios de las actualizaciones de detección y pase solo esos UID a RunTestsAsync. No utilice nombres visibles como identificadores de prueba.
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();
}
}
}
El asistente inicia un proceso de prueba independiente para la detección y ejecución, por lo que no requiere MtpServerCapabilities.MultiRequestSupport. Para reutilizar un proceso, compruebe esa funcionalidad después de InitializeAsync y antes de emitir varias solicitudes. Compruebe también SupportsDiscovery antes de solicitar el descubrimiento.
Importante
Los auxiliares de lanzamiento del cliente de solo origen administran una conexión JSON-RPC. En la versión 2.5.2, no aceptan ni enrutan las conexiones adicionales que usan las extensiones controladas por el controlador. Por ejemplo, los informes de TRX en Windows pueden iniciar un controlador cuya conexión pasiva llega antes del host de prueba activo. Una conexión con MultiConnectionProvider establecida en true solo envía actualizaciones y artefactos; no envíe solicitudes de detección ni solicitudes de ejecución a ella. El ejemplo rechaza esa conexión.
En el caso de los informes basados en controlador, ejecute la aplicación de prueba a través de la CLI o dotnet test, o use un cliente de herramientas que gestione las conexiones adicionales del protocolo. Un cliente de conexión única todavía puede recibir artefactos que publica el host de prueba activo.
Los proyectos de consola de C# y Visual Basic hacen referencia al proyecto auxiliar de C#:
using TestClientSample;
if (args.Length != 1)
{
Console.Error.WriteLine("Usage: DiscoverAndRun <test-application-path>");
return 1;
}
return await TestClient.DiscoverAndRunAsync(args[0]);
Ejecute cualquiera de los proyectos de consola con la ruta de acceso de la aplicación de prueba como argumento:
dotnet run --project <client-project> -- <test-application-path>
El ejemplo devuelve 0 cuando cada UID seleccionado tiene un resultado final y cada resultado final no reemplazado indica passed o skipped, 2 para resultados finales incorrectos o que faltan, y 8 cuando la detección no devuelve pruebas. Estas decisiones pertenecen al cliente de ejemplo. Una tarea completada RunTestsAsync significa que se completó la solicitud de protocolo, no que se hayan superado las pruebas.
MtpRunResult.Artifacts contiene artefactos de ejecución, no un veredicto de aprobado/reprobado. Utilice las actualizaciones del nodo de prueba para determinar los resultados de las pruebas.
Varios resultados pueden compartir un UID. En el ejemplo se omite un reintento anterior solo cuando retry.is-superseded es true; La igualdad de UID por sí sola no establece un reintento. Un resultado posterior aprobado u omitido no puede borrar un fallo anterior no sustituido.
Consumir actualizaciones y metadatos
Suscríbase a TestNodesUpdated antes del descubrimiento o la ejecución. Lea Changes de cada actualización e identifique los nodos por Uid. El cliente proporciona propiedades útiles, como DisplayName, ExecutionState, ErrorMessagey DurationInMilliseconds. Consulte el diccionario Node para otros campos serializados, incluidos traits y las propiedades específicas del framework.
MSTest publica las categorías de las pruebas y los valores personalizados de TestProperty como atributos. La disponibilidad de metadatos depende del marco de pruebas y de lo que publica durante la detección. Los atributos arbitrarios y las implementaciones en memoria IProperty no cruzan automáticamente el límite entre procesos. Para publicar metadatos de clave-valor portátiles, use el tipo público TestMetadataProperty . Las capas de plataforma y compatibilidad pueden proporcionar otros campos serializables; sus tipos de propiedades internos no son API de extensión. Para obtener más información, consulte Propiedades de nodo de prueba.
No espere que el diccionario de nodos JSON-RPC coincida con el documento --list-tests json. Los mecanismos de transporte utilizan disposiciones de campos diferentes. Además, no suponga que todos los marcos emiten los campos específicos vstest.* de VSTest.
El cliente procesa las actualizaciones en orden e invoca a los controladores correspondientes antes de que se complete la tarea de detección o ejecución. No necesita esperar a la actualización final. Los controladores se ejecutan de forma sincrónica en el bucle de lectura del cliente, por lo que no los bloquee ni espere sincrónicamente a otra operación de cliente. Dirija las tareas costosas o las actualizaciones de la interfaz de usuario al subproceso adecuado.
Pase un token de cancelación a los métodos de detección o ejecución para solicitar la cancelación mediante el protocolo. Si el marco de pruebas admite filtros de árbol, use DiscoverTestsWithFilterAsync o RunTestsWithFilterAsync con una expresión de filtro de árbol MTP para seleccionar pruebas sin crear primero una colección UID. Estos métodos no aceptan expresiones VSTest --filter.
Cumplir los requisitos del cliente
Use C# 12 o una versión posterior. El paquete respeta la configuración de versión del idioma de tu proyecto. Si el marco de trabajo de destino tiene como valor predeterminado una versión anterior de C#, establezca LangVersion en 12.0 o una versión posterior. El paquete también proporciona tipos de compatibilidad internos para .NET Framework 4.6.2 y netstandard2.0.
Quite cualquier copia escrita a mano del cliente MTP antes de agregar el paquete para evitar tipos internos duplicados.
Elección de un modelo de inicio
Para iniciar la aplicación de prueba como un proceso externo, use MtpServerClient.LaunchAsync. El cliente es el propietario de ese proceso hijo y lo cierra durante la desactivación cuando es necesario.
Para configurar una aplicación de prueba externa para un entorno determinado, establezca MtpServerClientOptions.EnvironmentVariables antes de iniciar la aplicación. El proceso hijo hereda el entorno del proceso padre, con los valores proporcionados aplicados al proceso hijo. Estas opciones no cambian el entorno de un host en proceso.
Cada cliente controla una aplicación de prueba. Para varias aplicaciones de prueba, cree un cliente para cada aplicación y agregue sus resultados en la herramienta.
Para un host embebido o un entorno con hilo de interfaz de usuario que no puede usar Process.Start, use MtpServerClient.LaunchInProcessAsync. El método ejecuta la aplicación de prueba a través de una devolución de llamada asincrónica en el proceso actual. No bloquee el hilo de inicio porque la función de devolución de llamada y el cliente comparten el proceso.
Ambos modelos de ejecución utilizan un transporte TCP de bucle cerrado. El navegador WebAssembly no puede crear el listener requerido, por lo que el inicio en el proceso genera PlatformNotSupportedException. La ejecución en proceso no ofrece un transporte alternativo de WebAssembly.
Apagar el cliente
Para desmontar el cliente y la aplicación iniciada sin bloquear el hilo de llamada, llame a IMtpServerClient.ShutdownAsync(). El método asíncrono evita problemas de capacidad de respuesta del hilo de la interfaz de usuario y de interbloqueo en hosts integrados.
En el caso de un host en proceso, MtpServerClientOptions.ServerShutdownTimeout controla cuánto tiempo espera la desmontaje después de que el cliente cierre el transporte. El valor predeterminado es 30 segundos. Lee ServerExitCode tras el apagado para obtener el código de salida de la llamada de retorno. El valor permanece null mientras la aplicación se ejecuta o cuando se produce un error en la aplicación en lugar de devolver un código de salida.
Declarar el estado del cliente
A partir de MTP 2.4.1, establezca la opción MtpServerClientOptions.IsStateful nullable para describir cómo el cliente consume actualizaciones de nodo de prueba:
| Value | Declaración |
|---|---|
true |
El cliente retiene un conjunto direccionable de nodos de prueba para la sesión y conserva el último estado conocido de cada nodo hasta que otra actualización lo cambie, como podría hacer el explorador de pruebas de un IDE. |
false |
El cliente consume actualizaciones sin conservar ese estado de nodo de prueba. |
null (valor predeterminado) |
El cliente no declara la funcionalidad. El protocolo de enlace de inicialización omite el campo. |
El cliente envía un valor booleano explícito a través de la capacidad del protocolo experimental capabilities.testing.isStateful. La persistencia de estado es independiente de la duración de la conexión y de la capacidad del servidor MultiRequestSupport. Un cliente no se vuelve con estado simplemente porque envía varias solicitudes a través de la misma conexión.
MTP 2.4.0 usa una opción booleana que no acepta valores NULL con un valor predeterminado de false. Para conservar una funcionalidad no declarada, use MTP 2.4.1 o posterior.
Dentro de un marco o extensión, use el servicio experimental IClientInfo para examinar la declaración. Lea clientInfo.Capabilities.IsStateful para obtener un resultado booleano conservador, que es false cuando la funcionalidad no está declarada. En MTP 2.4.1 o posterior, ClientCapabilitiesExtensions.GetIsStateful() conserva la distinción entre clientes no declarados, explícitamente sin estado y con estado.
Negociación de la compatibilidad del protocolo
A partir de MTP 2.4, la negociación del protocolo en modo servidor es independiente de la versión del paquete NuGet MTP. Un cliente declara las versiones de protocolo que admite y el servidor selecciona una versión común. Cuando no coincide ninguna versión compatible, la aplicación de prueba finaliza con el código de salida ProtocolError (12) en lugar de continuar con un contrato de comunicación incompatible.
Utilice el esquema JSON versionado de modo servidor 1.0 para validar los mensajes. Los serializadores System.Text.Json y Jsonite siguen el mismo esquema y la misma estructura de error.