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.
La plataforma de pruebas ofrece servicios valiosos tanto para el marco de pruebas como para los puntos de extensión. Estos servicios satisfacen las necesidades comunes, como el acceso a la configuración, el análisis y la recuperación de argumentos de la línea de comandos, la obtención del generador de registros y el acceso al sistema de registro, entre otros.
IServiceProvider
implementa el patrón de localizador de servicios para la plataforma de pruebas.
El IServiceProvider
se deriva directamente de la biblioteca de clases base.
namespace System
{
public interface IServiceProvider
{
object? GetService(Type serviceType);
}
}
La plataforma de pruebas ofrece métodos de extensión útiles para acceder a objetos de servicio conocidos. Todos estos métodos se hospedan en una clase estática dentro del espacio de nombres Microsoft.Testing.Platform.Services
.
public static class ServiceProviderExtensions
{
public static TService GetRequiredService<TService>(
this IServiceProvider provider)
public static TService? GetService<TService>(
this IServiceProvider provider)
public static IMessageBus GetMessageBus(
this IServiceProvider serviceProvider)
public static IConfiguration GetConfiguration(
this IServiceProvider serviceProvider)
public static ICommandLineOptions GetCommandLineOptions(
this IServiceProvider serviceProvider)
public static ILoggerFactory GetLoggerFactory(
this IServiceProvider serviceProvider)
public static IOutputDevice GetOutputDevice(
this IServiceProvider serviceProvider)
// ... and more
}
La mayoría de las fábricas de registro expuestas por puntos de extensión proporcionan acceso al IServiceProvider
: Por ejemplo, al registrar el framework de pruebas, el IServiceProvider
se pasa como parámetro al método de la fábrica.
ITestApplicationBuilder RegisterTestFramework(
Func<IServiceProvider, ITestFrameworkCapabilities> capabilitiesFactory,
Func<ITestFrameworkCapabilities, IServiceProvider, ITestFramework> adapterFactory);
En el código anterior, tanto el capabilitiesFactory
como el adapterFactory
proporcionan el IServiceProvider
como parámetro.
El servicio IConfiguration
La interfaz IConfiguration
se puede recuperar mediante el IServiceProvider
y proporciona acceso a los valores de configuración para el marco de pruebas y los puntos de extensión. De forma predeterminada, estas configuraciones se cargan desde:
- Variables de entorno
- Un archivo JSON llamado
[assemblyName].testingplatformconfig.json
ubicado cerca del ensamblaje del punto de entrada.
Se mantiene el orden de prioridad, lo que significa que si se encuentra una configuración en las variables de entorno, el archivo JSON no se procesará.
La interfaz es un sencillo par clave-valor de cadenas de texto.
public interface IConfiguration
{
string? this[string key] { get; }
}
Archivo de configuración JSON
El archivo JSON sigue una estructura jerárquica. Para acceder a las propiedades hijas, es necesario utilizar el separador :
. Por ejemplo, considere una configuración para un posible marco de pruebas, como:
{
"CustomTestingFramework": {
"DisableParallelism": true
}
}
El fragmento de código tendría un aspecto similar al siguiente:
IServiceProvider serviceProvider = null; // Get the service provider...
var configuration = serviceProvider.GetConfiguration();
if (bool.TryParse(configuration["CustomTestingFramework:DisableParallelism"], out var value) && value is true)
{
// ...
}
En el caso de una matriz, como:
{
"CustomTestingFramework": {
"Engine": [
"ThreadPool",
"CustomThread"
]
}
}
La sintaxis para acceder al primer elemento ("ThreadPool") es:
IServiceProvider serviceProvider = null; // Get the service provider...
var configuration = serviceProvider.GetConfiguration();
var fistElement = configuration["CustomTestingFramework:Engine:0"];
Variables de entorno
El separador de :
no funciona con claves jerárquicas de variables de entorno en todas las plataformas.
__
, el doble guion bajo, es:
- Compatible con todas las plataformas. Por ejemplo, el separador de
:
no es compatible con Bash, pero__
es . - Reemplazado automáticamente por un
:
Por ejemplo, la variable de entorno se puede establecer de la siguiente manera (este ejemplo es aplicable a Windows):
setx CustomTestingFramework__DisableParallelism=True
Puede optar por no usar el origen de configuración de la variable de entorno al crear el ITestApplicationBuilder
:
var options = new TestApplicationOptions();
options.Configuration.ConfigurationSources.RegisterEnvironmentVariablesConfigurationSource = false;
var builder = await TestApplication.CreateBuilderAsync(args, options);
El servicio ICommandLineOptions
El servicio ICommandLineOptions
se usa para capturar detalles sobre las opciones de línea de comandos que la plataforma ha analizado. Las API disponibles incluyen:
public interface ICommandLineOptions
{
bool IsOptionSet(string optionName);
bool TryGetOptionArgumentList(
string optionName,
out string[]? arguments);
}
El ICommandLineOptions
se puede obtener a través de determinadas API, como el ICommandLineOptionsProvider, o puede recuperar una instancia de este desde el IServiceProvider a través del método de extensión serviceProvider.GetCommandLineOptions()
.
ICommandLineOptions.IsOptionSet(string optionName)
: este método permite comprobar si se ha especificado una opción específica. Al especificar el optionName
, omita el prefijo --
. Por ejemplo, si el usuario introduce --myOption
, simplemente debe pasar myOption
.
ICommandLineOptions.TryGetOptionArgumentList(string optionName, out string[]? arguments)
: este método permite comprobar si se ha establecido una opción específica y, si es así, recuperar el valor o los valores correspondientes (si la aridad es más de una). De forma similar al caso anterior, el optionName
debe proporcionarse sin el prefijo --
.
El servicio ILoggerFactory
La plataforma de pruebas incluye un sistema de registro integrado que genera un archivo de registro. Puede ver las opciones de registro ejecutando el comando --help
.
Entre las opciones que puede elegir se incluyen:
--diagnostic Enable the diagnostic logging. The default log level is 'Trace'. The file will be written in the output directory with the name log_[MMddHHssfff].diag
--diagnostic-filelogger-synchronouswrite Force the built-in file logger to write the log synchronously. Useful for scenario where you don't want to lose any log (i.e. in case of crash). Note that this is slowing down the test execution.
--diagnostic-output-directory Output directory of the diagnostic logging, if not specified the file will be generated inside the default 'TestResults' directory.
--diagnostic-output-fileprefix Prefix for the log file name that will replace '[log]_.'
--diagnostic-verbosity Define the level of the verbosity for the --diagnostic. The available values are 'Trace', 'Debug', 'Information', 'Warning', 'Error', and 'Critical'
Desde una perspectiva de codificación, para registrar información, es necesario obtener el ILoggerFactory
del IServiceProvider
.
La API de ILoggerFactory
es la siguiente:
public interface ILoggerFactory
{
ILogger CreateLogger(string categoryName);
}
public static class LoggerFactoryExtensions
{
public static ILogger<TCategoryName> CreateLogger<TCategoryName>(this ILoggerFactory factory);
}
La fábrica de registro permite crear un objeto ILogger
utilizando la API CreateLogger
. También hay una API cómoda que acepta un argumento genérico, que se usará como nombre de categoría.
public interface ILogger
{
Task LogAsync<TState>(
LogLevel logLevel,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter);
void Log<TState>(
LogLevel logLevel,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter);
bool IsEnabled(LogLevel logLevel);
}
public interface ILogger<out TCategoryName> : ILogger
{
}
public static class LoggingExtensions
{
public static Task LogCriticalAsync(this ILogger logger, string message);
public static Task LogDebugAsync(this ILogger logger, string message);
public static Task LogErrorAsync(this ILogger logger, Exception ex);
public static Task LogErrorAsync(this ILogger logger, string message, Exception ex);
public static Task LogErrorAsync(this ILogger logger, string message);
public static Task LogInformationAsync(this ILogger logger, string message);
public static Task LogTraceAsync(this ILogger logger, string message);
public static Task LogWarningAsync(this ILogger logger, string message);
public static void LogCritical(this ILogger logger, string message);
public static void LogDebug(this ILogger logger, string message);
public static void LogError(this ILogger logger, Exception ex);
public static void LogError(this ILogger logger, string message, Exception ex);
public static void LogError(this ILogger logger, string message);
public static void LogInformation(this ILogger logger, string message);
public static void LogTrace(this ILogger logger, string message);
public static void LogWarning(this ILogger logger, string message);
}
El objeto ILogger
, creado por el ILoggerFactory
, ofrece API para registrar información en varios niveles. Estos niveles de registro incluyen:
public enum LogLevel
{
Trace,
Debug,
Information,
Warning,
Error,
Critical,
None,
}
Este es un ejemplo de cómo podría usar la API de registro:
...
IServiceProvider provider = null; // Get the service provider...
var factory = provider.GetLoggerFactory();
var logger = factory.CreateLogger<TestingFramework>();
// ...
if (logger.IsEnabled(LogLevel.Information))
{
await logger.LogInformationAsync(
$"Executing request of type '{context.Request}'");
}
// ...
Tenga en cuenta que, para evitar asignaciones innecesarias, debe comprobar si el nivel está habilitado mediante la API de ILogger.IsEnabled(LogLevel)
.
El servicio IMessageBus
El servicio message bus es el mecanismo central que facilita el intercambio de información entre el marco de pruebas y sus extensiones.
El bus de mensajes de la plataforma de pruebas emplea el patrón publish-subscribe.
La estructura general del bus compartido es la siguiente:
Como se muestra en el diagrama, que incluye extensiones y un marco de pruebas, hay dos posibles acciones: insertar información en el bus o consumir información del bus.
El IMessageBus
satisface el acción de inserción en el bus y la API es:
public interface IMessageBus
{
Task PublishAsync(
IDataProducer dataProducer,
IData data);
}
public interface IDataProducer : IExtension
{
Type[] DataTypesProduced { get; }
}
public interface IData
{
string DisplayName { get; }
string? Description { get; }
}
Tenga en cuenta los siguientes detalles sobre los parámetros:
IDataProducer
: elIDataProducer
comunica al bus de mensajes elType
de información que puede proporcionar y establece la propiedad a través de la herencia de la interfaz base IExtension. Esto implica que no se pueden insertar datos indiscriminadamente en el bus de mensajes; Debe declarar el tipo de datos generado de antemano. Si inserta datos inesperados, se desencadenará una excepción.IData
: esta interfaz actúa como marcador de posición donde solo necesita proporcionar detalles descriptivos, como el nombre y una descripción. La interfaz no revela mucho sobre la naturaleza de los datos, que es intencionada. Implica que el marco de pruebas y las extensiones pueden insertar cualquier tipo de datos en el bus, y estos datos pueden ser consumidos por cualquier extensión registrada o el propio marco de pruebas.
Este enfoque facilita la evolución del proceso de intercambio de información, lo que impide cambios importantes cuando una extensión no está familiarizada con los nuevos datos. Permite que diferentes versiones de extensiones y el marco de pruebas funcionen en armonía, en función de su comprensión mutua.
El extremo opuesto del bus se denomina consumidor, que está suscrito a un tipo específico de datos y, por tanto, puede consumirlos.
Importante
Use siempre espera la llamada a PublishAsync
. Si no lo hace, es posible que la plataforma de prueba y las extensiones no procesen correctamente la IData
, lo que podría provocar errores sutiles. Es solo después de regresar del proceso de espera que puede tener la certeza de que el IData
se ha puesto en cola para su procesamiento en el bus de mensajes. Independientemente del punto de extensión en el que esté trabajando, asegúrese de que ha esperado todas las llamadas PublishAsync
antes de salir de la extensión. Por ejemplo, si está implementando el testing framework
, no debe llamar a Complete
en las solicitudes hasta que haya esperado todas las llamadas PublishAsync
para esa solicitud específica.
El servicio IOutputDevice
La plataforma de pruebas encapsula la idea de un dispositivo de salida, lo que permite que el marco de pruebas y las extensiones presenten información transmitiendo cualquier tipo de datos al sistema de visualización utilizado actualmente.
El ejemplo más tradicional de un dispositivo de salida es la salida de la consola.
Nota
Aunque la plataforma de pruebas está diseñada para admitir dispositivos de salida personalizados, actualmente este punto de extensión no está disponible.
Para transmitir datos al dispositivo de salida, debe obtener el IOutputDevice
del IServiceProvider
.
La API consta de:
public interface IOutputDevice
{
Task DisplayAsync(
IOutputDeviceDataProducer producer,
IOutputDeviceData data);
}
public interface IOutputDeviceDataProducer : IExtension
{
}
public interface IOutputDeviceData
{
}
El IOutputDeviceDataProducer
extiende el IExtension
y proporciona información sobre el remitente al dispositivo de salida de .
El IOutputDeviceData
actúa como una interfaz de marcador de posición. El concepto de IOutputDevice
es dar cabida a información más compleja que simplemente texto coloreado. Por ejemplo, podría ser un objeto complejo que se pueda representar gráficamente.
La plataforma de pruebas, de forma predeterminada, ofrece un modelo de texto de color tradicional para el objeto IOutputDeviceData
:
public class TextOutputDeviceData : IOutputDeviceData
{
public TextOutputDeviceData(string text)
public string Text { get; }
}
public sealed class FormattedTextOutputDeviceData : TextOutputDeviceData
{
public FormattedTextOutputDeviceData(string text)
public IColor? ForegroundColor { get; init; }
public IColor? BackgroundColor { get; init; }
}
public sealed class SystemConsoleColor : IColor
{
public ConsoleColor ConsoleColor { get; init; }
}
Aquí tienes un ejemplo de cómo puedas usar el texto en color con el dispositivo de salida activo :
IServiceProvider provider = null; // Get the service provider...
var outputDevice = provider.GetOutputDevice();
await outputDevice.DisplayAsync(
this,
new FormattedTextOutputDeviceData($"TestingFramework version '{Version}' running tests with parallelism of {_dopValue}")
{
ForegroundColor = new SystemConsoleColor
{
ConsoleColor = ConsoleColor.Green
}
});
Además del uso estándar de texto coloreado, la principal ventaja de IOutputDevice
y IOutputDeviceData
es que el dispositivo de salida de es completamente independiente y desconocido para el usuario. Esto permite el desarrollo de interfaces de usuario complejas. Por ejemplo, es totalmente factible implementar una aplicación web en tiempo real de que demuestre el progreso de las pruebas.
El servicio IPlatformInformation
Proporciona información sobre la plataforma, como: nombre, versión, hash de confirmación y fecha de compilación.