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.
En este artículo se tratan los puntos de extensibilidad de Microsoft.Testing.Platform más allá del propio marco de pruebas. Para la creación del marco de pruebas, consulte Compilación de un marco de pruebas.
Para obtener el resumen completo del punto de extensión y los conceptos dentro del proceso o fuera del proceso, consulte Creación de extensiones personalizadas.
Puntos de extensibilidad
La plataforma de pruebas proporciona puntos de extensibilidad adicionales que permiten personalizar el comportamiento de la plataforma y el marco de pruebas. Estos puntos de extensibilidad son opcionales y se pueden usar para mejorar la experiencia de prueba.
Las extensiones ICommandLineOptionsProvider
Nota:
Al extender esta API, la extensión personalizada existirá tanto dentro como fuera del proceso de host de prueba.
Como se describe en la sección arquitectura, el paso inicial implica crear ITestApplicationBuilder para registrar el marco de pruebas y las extensiones con él.
var builder = await TestApplication.CreateBuilderAsync(args);
El método CreateBuilderAsync acepta una matriz de cadenas (string[]) denominada args. Estos argumentos se pueden usar para pasar opciones de línea de comandos a todos los componentes de la plataforma de pruebas (incluidos los componentes integrados, los marcos de pruebas y las extensiones), lo que permite personalizar su comportamiento.
Normalmente, los argumentos pasados son los recibidos en el método Main(string[] args) estándar . Sin embargo, si el entorno de hosting difiere, se puede proporcionar cualquier lista de argumentos.
Los argumentos deben ir precedidos por un doble guion --. Por ejemplo: --filter.
Si un componente como un marco de pruebas o un punto de extensión desea ofrecer opciones de línea de comandos personalizadas, puede hacerlo implementando la interfaz ICommandLineOptionsProvider. A continuación, esta implementación se puede registrar con ITestApplicationBuilder a través de la fábrica de registro de la propiedad CommandLine, como se muestra aquí:
builder.CommandLine.AddProvider(
static () => new CustomCommandLineOptions());
En el ejemplo proporcionado, CustomCommandLineOptions es una implementación de la interfaz ICommandLineOptionsProvider. Esta interfaz consta de los siguientes miembros y tipos de datos:
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);
}
Como se observa, ICommandLineOptionsProvider extiende la interfaz IExtension. Por lo tanto, al igual que cualquier otra extensión, puede optar por habilitarla o deshabilitarla mediante la API IExtension.IsEnabledAsync.
El orden de ejecución de ICommandLineOptionsProvider es:
Vamos a examinar las APIs y su significado.
ICommandLineOptionsProvider.GetCommandLineOptions(): este método se utiliza para recuperar todas las opciones que ofrece el componente. Cada CommandLineOption requiere que se especifiquen las siguientes propiedades:
string name: Este es el nombre de la opción, presentado sin guion. Por ejemplo, los usuarios utilizarían el filtro como --filter.
string description: se trata de una descripción de la opción. Se mostrará cuando los usuarios pasen --help como argumento al generador de aplicaciones.
ArgumentArity arity: la aridad de una opción es el número de valores que se pueden pasar si se especifica esa opción o comando. Las aridades actualmente disponibles son:
-
Zero: representa una aridad de argumentos de cero. -
ZeroOrOne: representa una aridad de argumento de cero o uno. -
ZeroOrMore: representa una aridad de argumentos de cero o más. -
OneOrMore: Representa una aridad de uno o más argumentos. -
ExactlyOne: representa una aridad de argumentos de exactamente uno.
Para ver ejemplos, consulte la tabla de aridades System.CommandLine.
bool isHidden: esta propiedad indica que la opción está disponible para su uso, pero no se mostrará en la descripción cuando se invoque --help.
ICommandLineOptionsProvider.ValidateOptionArgumentsAsync: este método se emplea para validar el argumento proporcionado por el usuario.
Por ejemplo, si tiene un parámetro denominado --dop que representa el grado de paralelismo de nuestro marco de pruebas personalizado, un usuario podría escribir --dop 0. En este escenario, el valor 0 no sería válido porque se espera que tenga un grado de paralelismo de 1 o más. Al usar ValidateOptionArgumentsAsync, puede realizar una validación por adelantado y devolver un mensaje de error si es necesario.
Una posible implementación del ejemplo anterior sería:
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: este método se llama en último lugar y permite hacer la comprobación de coherencia global.
Por ejemplo, supongamos que nuestro marco de pruebas tiene la capacidad de generar un informe de resultados de prueba y guardarlo en un archivo. Se obtiene acceso a esta característica mediante la opción --generatereport y el nombre de archivo se especifica con --reportfilename myfile.rep. En este escenario, si un usuario solo proporciona la opción --generatereport sin especificar un nombre de archivo, se producirá un error en la validación porque el informe no se puede generar sin un nombre de archivo.
Una posible implementación del ejemplo anterior sería:
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;
}
Tenga en cuenta que el método ValidateCommandLineOptionsAsync proporciona el servicio ICommandLineOptions, que se usa para capturar la información del argumento analizada por la propia plataforma.
Las extensiones ITestSessionLifetimeHandler
ITestSessionLifeTimeHandler es una extensión dentro del proceso que permite la ejecución del código antes y después de la sesión de prueba.
Para registrar un ITestSessionLifeTimeHandler personalizado, use la API siguiente:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddTestSessionLifetimeHandle(
static serviceProvider => new CustomTestSessionLifeTimeHandler());
La fábrica utiliza IServiceProvider para obtener acceso al conjunto de servicios ofrecidos por la plataforma de pruebas.
Importante
La secuencia de registro es significativa, ya que las API se llaman en el orden en que se registraron.
La interfaz ITestSessionLifeTimeHandler incluye los métodos siguientes:
public interface ITestSessionLifetimeHandler : ITestHostExtension
{
Task OnTestSessionStartingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken);
Task OnTestSessionFinishingAsync(
SessionUid sessionUid,
CancellationToken cancellationToken);
}
public readonly struct SessionUid(string value)
{
public string Value { get; } = value;
}
public interface ITestHostExtension : IExtension
{
}
ITestSessionLifetimeHandler es un tipo de ITestHostExtension, que actúa como base para todas las extensiones del host de prueba. Al igual que todos los demás puntos de extensión, también hereda de IExtension. Por lo tanto, al igual que cualquier otra extensión, puede optar por habilitarla o deshabilitarla mediante la API IExtension.IsEnabledAsync.
Tenga en cuenta los detalles siguientes para esta API:
OnTestSessionStartingAsync: este método se invoca antes del inicio de la sesión de prueba y recibe el objeto SessionUid, que proporciona un identificador opaco para la sesión de prueba actual.
OnTestSessionFinishingAsync: este método se invoca después de la finalización de la sesión de prueba, lo que garantiza que el marco de pruebas ha terminado de ejecutar todas las pruebas y ha notificado todos los datos pertinentes a la plataforma. Normalmente, en este método, la extensión emplea IMessageBus para transmitir recursos o datos personalizados al bus de plataforma compartida. Este método también puede indicar a cualquier extensión personalizada fuera de proceso que la sesión de prueba ha concluido.
Por último, ambas API toman un valor CancellationToken que se espera que la extensión respete.
Si la extensión requiere una inicialización intensiva y necesita usar el patrón async/await, puede hacer referencia a Async extension initialization and cleanup. Si necesita compartir el estado entre puntos de extensión, consulte la sección CompositeExtensionFactory<T>.
Las extensiones ITestApplicationLifecycleCallbacks
ITestApplicationLifecycleCallbacks es una extensión dentro del proceso que permite la ejecución del código antes de todo; es como tener acceso a la primera línea del hipotético main del host de prueba.
Para registrar un ITestApplicationLifecycleCallbacks personalizado, use la API siguiente:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddTestApplicationLifecycleCallbacks(
static serviceProvider
=> new CustomTestApplicationLifecycleCallbacks());
La fábrica utiliza IServiceProvider para obtener acceso al conjunto de servicios ofrecidos por la plataforma de pruebas.
Importante
La secuencia de registro es significativa, ya que las API se llaman en el orden en que se registraron.
La interfaz ITestApplicationLifecycleCallbacks incluye los métodos siguientes:
public interface ITestApplicationLifecycleCallbacks : ITestHostExtension
{
Task BeforeRunAsync(CancellationToken cancellationToken);
Task AfterRunAsync(
int exitCode,
CancellationToken cancellation);
}
public interface ITestHostExtension : IExtension
{
}
ITestApplicationLifecycleCallbacks es un tipo de ITestHostExtension, que actúa como base para todas las extensiones del host de prueba. Al igual que todos los demás puntos de extensión, también hereda de IExtension. Por lo tanto, al igual que cualquier otra extensión, puede optar por habilitarla o deshabilitarla mediante la API IExtension.IsEnabledAsync.
BeforeRunAsync: este método actúa como punto de contacto inicial para el host de prueba y es la primera oportunidad para que una extensión dentro del proceso ejecute una característica. Normalmente se usa para establecer una conexión con las extensiones fuera de proceso correspondientes, si una característica está diseñada para funcionar en ambos entornos.
Por ejemplo, la característica de volcado de memoria de bloqueo integrada se compone tanto de extensiones dentro del proceso como fuera del proceso, y este método se usa para intercambiar información con el componente fuera del proceso de la extensión.
AfterRunAsync: este método es la llamada final antes de salir de int ITestApplication.RunAsync() y proporciona exit code. Debe usarse únicamente para tareas de limpieza y notificar a cualquier extensión fuera del proceso correspondiente que el host de prueba está a punto de finalizar.
Por último, ambas API toman un valor CancellationToken que se espera que la extensión respete.
Las extensiones IDataConsumer
IDataConsumer es una extensión dentro del proceso capaz de suscribirse y recibir IData información que se inserta en IMessageBus por medio del marco de prueba y sus extensiones.
Este punto de extensión es fundamental, ya que permite a los desarrolladores recopilar y procesar toda la información generada durante una sesión de prueba.
Para registrar un IDataConsumer personalizado, use la API siguiente:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHost.AddDataConsumer(
static serviceProvider => new CustomDataConsumer());
La fábrica utiliza IServiceProvider para obtener acceso al conjunto de servicios ofrecidos por la plataforma de pruebas.
Importante
La secuencia de registro es significativa, ya que las API se llaman en el orden en que se registraron.
La interfaz IDataConsumer incluye los métodos siguientes:
public interface IDataConsumer : ITestHostExtension
{
Type[] DataTypesConsumed { get; }
Task ConsumeAsync(
IDataProducer dataProducer,
IData value,
CancellationToken cancellationToken);
}
public interface IData
{
string DisplayName { get; }
string? Description { get; }
}
IDataConsumer es un tipo de ITestHostExtension, que actúa como base para todas las extensiones del host de prueba. Al igual que todos los demás puntos de extensión, también hereda de IExtension. Por lo tanto, al igual que cualquier otra extensión, puede optar por habilitarla o deshabilitarla mediante la API IExtension.IsEnabledAsync.
DataTypesConsumed: esta propiedad devuelve una lista de Type que esta extensión tiene planificado consumir. Se corresponde con IDataProducer.DataTypesProduced. En particular, IDataConsumer puede suscribirse a varios tipos que se originan en instancias diferentes IDataProducer sin problemas.
ConsumeAsync: este método se activa cada vez que se insertan en el IMessageBus datos de un tipo al que el consumidor actual está suscrito. Recibe IDataProducer para proporcionar detalles sobre el productor de la carga de datos, así como sobre la carga de datos IData en sí misma. Como puede ver, IData es una interfaz genérica de marcador de posición que contiene datos informativos generales. La capacidad de insertar diferentes tipos de IData implica que el consumidor debe cambiar en el propio tipo para convertirlo al tipo correcto y acceder a la información específica.
Una implementación de ejemplo de un consumidor que quiere elaborar el TestNodeUpdateMessage generado por un marco de pruebas podría ser:
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;
}
...
}
Por último, la API toma un CancellationToken que se espera que la extensión respete.
Importante
Es fundamental procesar la carga directamente dentro del método ConsumeAsync.
IMessageBus puede administrar el procesamiento sincrónico y asincrónico, coordinando la ejecución con el marco de pruebas. Aunque el proceso de consumo es completamente asincrónico y no bloquea IMessageBus.Push en el momento de escribirlo, se trata de un detalle de implementación que puede cambiar en el futuro debido a requisitos futuros. Sin embargo, la plataforma garantiza que este método se llame siempre una vez, lo que elimina la necesidad de una sincronización compleja, además de administrar la escalabilidad de los consumidores.
Advertencia
Cuando se usa IDataConsumer junto con ITestHostProcessLifetimeHandler dentro de un punto de extensión compuesto, es fundamental ignorar los datos recibidos después de la ejecución de ITestSessionLifetimeHandler.OnTestSessionFinishingAsync.
OnTestSessionFinishingAsync es la oportunidad final de procesar datos acumulados y transmitir nueva información a IMessageBus, por lo que los datos consumidos más allá de este punto no serán utilizables por la extensión.
Si la extensión requiere una inicialización intensiva y necesita usar el patrón async/await, puede hacer referencia a Async extension initialization and cleanup. Si necesita compartir el estado entre puntos de extensión, consulte la sección CompositeExtensionFactory<T>.
Las extensiones ITestHostEnvironmentVariableProvider
ITestHostEnvironmentVariableProvider es una extensión fuera del proceso que permite establecer variables de entorno personalizadas para el host de prueba. El uso de este punto de extensión garantiza que la plataforma de prueba iniciará un nuevo host con las variables de entorno adecuadas, como se detalla en la sección arquitectura .
Para registrar un ITestHostEnvironmentVariableProvider personalizado, use la API siguiente:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHostControllers.AddEnvironmentVariableProvider(
static serviceProvider => new CustomEnvironmentVariableForTestHost());
La fábrica utiliza IServiceProvider para obtener acceso al conjunto de servicios ofrecidos por la plataforma de pruebas.
Importante
La secuencia de registro es significativa, ya que las API se llaman en el orden en que se registraron.
La interfaz ITestHostEnvironmentVariableProvider incluye los métodos y tipos siguientes:
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 es un tipo de ITestHostControllersExtension, que actúa como base para todas las extensiones del controlador del host de prueba. Al igual que todos los demás puntos de extensión, también hereda de IExtension. Por lo tanto, al igual que cualquier otra extensión, puede optar por habilitarla o deshabilitarla mediante la API IExtension.IsEnabledAsync.
Tenga en cuenta los detalles de esta API:
UpdateAsync: esta API de actualización proporciona una instancia del objeto IEnvironmentVariables, desde el que puede llamar a los métodos SetVariable o RemoveVariable. Al usar SetVariable, debe pasar un objeto de tipo EnvironmentVariable, que requiere las siguientes especificaciones:
-
Variable: el nombre de la variable de entorno. -
Value: valor de la variable de entorno. -
IsSecret: indica si la variable de entorno contiene información confidencial que no se debe registrar ni obtener acceso a ella a través deTryGetVariable. -
IsLocked: determina si otras extensionesITestHostEnvironmentVariableProviderpueden modificar este valor.
ValidateTestHostEnvironmentVariablesAsync: este método se invoca después de llamar a todos los métodos UpdateAsync de las instancias registradas ITestHostEnvironmentVariableProvider . Permite comprobar la configuración correcta de las variables de entorno. Toma un objeto que implementa IReadOnlyEnvironmentVariables, que proporciona el método TryGetVariable para capturar información de variable de entorno específica con el tipo de objeto OwnedEnvironmentVariable. Después de la validación, se devuelve ValidationResult que contiene todos los motivos de error.
Nota:
De forma predeterminada, la plataforma de prueba implementa y registra SystemEnvironmentVariableProvider. Este proveedor carga todas las variables de entorno actuales. Como primer proveedor registrado, se ejecuta primero, concediendo acceso a las variables de entorno predeterminadas para todas las demás extensiones de usuario ITestHostEnvironmentVariableProvider.
Si la extensión requiere una inicialización intensiva y necesita usar el patrón async/await, puede hacer referencia a Async extension initialization and cleanup. Si necesita compartir el estado entre puntos de extensión, consulte la sección CompositeExtensionFactory<T>.
Las extensiones ITestHostProcessLifetimeHandler
ITestHostProcessLifetimeHandler es una extensión fuera del proceso que permite observar el proceso de host de prueba desde un punto de vista externo. Esto garantiza que la extensión no se vea afectada por posibles bloqueos o errores inducidos por el código que se está probando. El uso de este punto de extensión solicitará a la plataforma de prueba que inicie un nuevo host, como se detalla en la sección arquitectura.
Para registrar un ITestHostProcessLifetimeHandler personalizado, use la API siguiente:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
builder.TestHostControllers.AddProcessLifetimeHandler(
static serviceProvider => new CustomMonitorTestHost());
La fábrica utiliza IServiceProvider para obtener acceso al conjunto de servicios ofrecidos por la plataforma de pruebas.
Importante
La secuencia de registro es significativa, ya que las API se llaman en el orden en que se registraron.
La interfaz ITestHostProcessLifetimeHandler incluye los métodos siguientes:
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 es un tipo de ITestHostControllersExtension, que actúa como base para todas las extensiones del controlador del host de prueba. Al igual que todos los demás puntos de extensión, también hereda de IExtension. Por lo tanto, al igual que cualquier otra extensión, puede optar por habilitarla o deshabilitarla mediante la API IExtension.IsEnabledAsync.
Tenga en cuenta los detalles siguientes para esta API:
BeforeTestHostProcessStartAsync: este método se invoca antes de que la plataforma de prueba inicie los hosts de prueba.
OnTestHostProcessStartedAsync: este método se invoca inmediatamente después de que se inicie el host de prueba. Este método ofrece un objeto que implementa la interfaz ITestHostProcessInformation, que proporciona detalles clave sobre el resultado del proceso del host de prueba.
Importante
La invocación de este método no detiene la ejecución del host de prueba. Si necesita ponerla en pausa, debe registrar una extensión dentro del proceso como ITestApplicationLifecycleCallbacks y sincronizarla con la extensión fuera de proceso.
OnTestHostProcessExitedAsync: este método se invoca cuando se completa la ejecución del conjunto de pruebas. Este método proporciona un objeto que se ajusta a la interfaz de ITestHostProcessInformation, la cual transmite detalles cruciales sobre el resultado del proceso del host de prueba.
La interfaz de ITestHostProcessInformation proporciona los siguientes detalles:
-
PID: ID de proceso del host de prueba. -
ExitCode: código de salida del proceso. Este valor solo está disponible en el métodoOnTestHostProcessExitedAsync. Si intenta acceder a él dentro del métodoOnTestHostProcessStartedAsync, se producirá una excepción. -
HasExitedGracefully: Un valor booleano que indica si el host de prueba ha fallado. Si es verdadero, significa que el host de prueba no salió de forma adecuada.
Orden de ejecución de extensiones
La plataforma de pruebas consta de un marco de pruebas y de cualquier número de extensiones que pueden funcionar dentro del proceso o fuera del proceso. Este documento describe la secuencia de llamadas a todos los puntos de extensibilidad potenciales para proporcionar claridad sobre cuándo se prevé que se invoque una característica:
- ITestHostEnvironmentVariableProvider.UpdateAsync: fuera del proceso
- ITestHostEnvironmentVariableProvider.ValidateTestHostEnvironmentVariablesAsync: fuera del proceso
- ITestHostProcessLifetimeHandler.BeforeTestHostProcessStartAsync: fuera del proceso
- Inicio del proceso de host de prueba
- ITestHostProcessLifetimeHandler.OnTestHostProcessStartedAsync: fuera del proceso, este evento puede entrelazar las acciones de las extensiones dentro del proceso, en función de las condiciones de carrera.
- ITestApplicationLifecycleCallbacks.BeforeRunAsync: En proceso
- ITestSessionLifetimeHandler.OnTestSessionStartingAsync: dentro del proceso
- ITestFramework.CreateTestSessionAsync: en proceso
- ITestFramework.ExecuteRequestAsync: dentro del proceso; se puede llamar a este método una o varias veces. En este momento, el marco de pruebas transmitirá información a IMessageBus que IDataConsumer puede usar.
- ITestFramework.CloseTestSessionAsync: en proceso
- ITestSessionLifetimeHandler.OnTestSessionFinishingAsync: dentro del proceso
- ITestApplicationLifecycleCallbacks.AfterRunAsync: dentro del proceso
- La limpieza dentro del proceso implica invocar a dispose e IAsyncCleanableExtension en todos los puntos de extensión.
- ITestHostProcessLifetimeHandler.OnTestHostProcessExitedAsync: fuera del proceso
- La limpieza de procesos externos implica llamar a dispose e IAsyncCleanableExtension en todos los puntos de extensión.
Ayudantes de extensiones
La plataforma de pruebas proporciona un conjunto de clases e interfaces auxiliares para simplificar la implementación de extensiones. Estos asistentes están diseñados para simplificar el proceso de desarrollo y asegurarse de que la extensión cumpla los estándares de la plataforma.
Inicialización asincrónica y limpieza de extensiones
La creación del marco de pruebas y las extensiones a través de factorías se adhiere al mecanismo estándar de creación de objetos .NET, que usa constructores sincrónicos. Si una extensión requiere una inicialización intensiva (por ejemplo, el acceso al sistema de archivos o la red), no puede emplear el patrón async/await en el constructor porque los constructores devuelven void, no Task.
Por lo tanto, la plataforma de pruebas proporciona un método para inicializar una extensión mediante el patrón async/await a través de una interfaz sencilla. En aras de la simetría, también ofrece una interfaz asincrónica para la limpieza que las extensiones pueden implementar sin complicaciones.
public interface IAsyncInitializableExtension
{
Task InitializeAsync();
}
public interface IAsyncCleanableExtension
{
Task CleanupAsync();
}
IAsyncInitializableExtension.InitializeAsync: Se garantiza que este método se invoque después de la factoría de creación.
IAsyncCleanableExtension.CleanupAsync: este método garantiza que se invoque al menos una vez durante la finalización de la sesión de prueba, antes del valor predeterminado DisposeAsync o Dispose.
Importante
De forma similar al método estándar Dispose , CleanupAsync se puede invocar varias veces. Si se llama más de una vez al método CleanupAsync de un objeto, el objeto debe omitir todas las llamadas después de la primera. El objeto no debe producir una excepción si se llama varias veces a su método CleanupAsync.
Nota:
De forma predeterminada, la plataforma de pruebas llamará a DisposeAsync si está disponible o Dispose si se implementa. Es importante tener en cuenta que la plataforma de pruebas no llamará a ambos métodos dispose, sino que dará prioridad al método async si se implementa.
CompositeExtensionFactory<T>
Como se describe en la sección extensiones, la plataforma de pruebas le permite implementar interfaces para incorporar extensiones personalizadas tanto dentro como fuera del proceso.
Cada interfaz aborda una característica determinada y, según .NET diseño, se implementa esta interfaz en un objeto específico. Puede registrar la propia extensión mediante la API AddXXX de registro específica desde el objeto TestHost o TestHostController desde ITestApplicationBuilder, tal y como se detalla en las secciones correspondientes.
Sin embargo, si debe compartir el estado entre dos extensiones, el hecho de que pueda implementar y registrar diferentes objetos implementando distintas interfaces, hace que compartir sea una tarea complicada. Sin ninguna ayuda, necesitaría una manera de pasar una extensión a la otra para compartir información, lo que complica el diseño.
Por lo tanto, la plataforma de pruebas proporciona un método sofisticado para implementar varios puntos de extensión con el mismo tipo, lo que hace que compartir datos sea una tarea sencilla. Lo único que debe hacer es usar CompositeExtensionFactory<T>, que luego se puede registrar con la misma API que utilizaría para una sola implementación de interfaz.
Por ejemplo, considere un tipo que implementa tanto ITestSessionLifetimeHandler como IDataConsumer. Este es un escenario común porque a menudo se desea recopilar información del marco de pruebas y, a continuación, cuando la sesión de pruebas concluya, se enviará el artefacto mediante IMessageBus dentro de ITestSessionLifetimeHandler.OnTestSessionFinishingAsync.
Lo que debe hacer es implementar las interfaces con normalidad:
internal class CustomExtension : ITestSessionLifetimeHandler, IDataConsumer, ...
{
...
}
Una vez que haya creado el CompositeExtensionFactory<CustomExtension> para su tipo, puede registrarlo con las APIs IDataConsumer y ITestSessionLifetimeHandler, que ofrecen una sobrecarga para CompositeExtensionFactory<T>:
var builder = await TestApplication.CreateBuilderAsync(args);
// ...
var factory = new CompositeExtensionFactory<CustomExtension>(serviceProvider => new CustomExtension());
builder.TestHost.AddTestSessionLifetimeHandle(factory);
builder.TestHost.AddDataConsumer(factory);
El constructor de fábrica emplea IServiceProvider para obtener acceso a los servicios proporcionados por la plataforma de pruebas.
La plataforma de pruebas será responsable de administrar el ciclo de vida de la extensión compuesta.
Es importante tener en cuenta que, debido a la compatibilidad de la plataforma de pruebas con las extensiones dentro del proceso y fuera del proceso, no se puede combinar ningún punto de extensión arbitrariamente. La creación y el uso de extensiones dependen del tipo de host, lo que significa que solo se pueden agrupar extensiones dentro del proceso (TestHost) y fuera del proceso (TestHostController).
Las siguientes combinaciones están disponibles:
- Para
ITestApplicationBuilder.TestHost, puede combinarIDataConsumeryITestSessionLifetimeHandler. - Para
ITestApplicationBuilder.TestHostControllers, puede combinarITestHostEnvironmentVariableProvideryITestHostProcessLifetimeHandler.