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.
MSTest proporciona un ciclo de vida bien definido para las clases de prueba y los métodos de prueba, lo que le permite realizar operaciones de configuración y desmontaje en varias fases de ejecución de pruebas. Comprender el ciclo de vida le ayuda a escribir pruebas eficaces y a evitar problemas comunes.
Introducción al ciclo de vida
El ciclo de vida se agrupa en cuatro fases, ejecutado desde el nivel más alto (ensamblado) hasta el nivel más bajo (método de prueba):
- Nivel de ensamblado: se ejecuta una vez cuando el ensamblado de prueba se carga y descarga
- Nivel de clase: se ejecuta una vez por clase de prueba
- Nivel de prueba global: se ejecuta antes y después de cada método de prueba del ensamblado
- Nivel de prueba: se ejecuta para cada método de prueba (incluida cada fila de datos en pruebas con parámetros)
Ciclo de vida a nivel de ensamblaje
Los métodos de ciclo de vida del ensamblado se ejecutan una vez cuando el ensamblado de prueba se carga y descarga. Úselos para una configuración única costosa, como la inicialización de la base de datos o el inicio del servicio.
AssemblyInitialize y AssemblyCleanup
[TestClass]
public class AssemblyLifecycleExample
{
private static IHost? _host;
[AssemblyInitialize]
public static async Task AssemblyInit(TestContext context)
{
// Runs once before any tests in the assembly
_host = await StartTestServerAsync();
context.WriteLine("Test server started");
}
[AssemblyCleanup]
public static async Task AssemblyCleanup(TestContext context)
{
// Runs once after all tests complete
// TestContext parameter available in MSTest 3.8+
if (_host != null)
{
await _host.StopAsync();
}
}
private static Task<IHost> StartTestServerAsync()
{
// Server initialization
return Task.FromResult<IHost>(null!);
}
}
Requisitos
- Los métodos deben ser
public static - Tipo de valor devuelto:
void,TaskoValueTask(MSTest v3.3+) -
AssemblyInitializerequiere unTestContextparámetro -
AssemblyCleanupacepta cero parámetros o unTestContextparámetro (MSTest 3.8+) - Solo se permite uno de cada atributo por cada ensamblaje
- Debe estar en una clase marcada con
[TestClass]
Sugerencia
Analizadores relacionados:
-
MSTEST0012 : valida la
AssemblyInitializefirma. -
MSTEST0013: valida la
AssemblyCleanupfirma.
Ciclo de vida a nivel de clase
Los métodos de ciclo de vida de clase se ejecutan una vez por clase de prueba, antes y después de todos los métodos de prueba de esa clase. Úselos para la configuración compartida entre tests en una clase.
ClassInitialize y ClassCleanup
[TestClass]
public class ClassLifecycleExample
{
private static HttpClient? _client;
[ClassInitialize]
public static void ClassInit(TestContext context)
{
// Runs once before any tests in this class
_client = new HttpClient
{
BaseAddress = new Uri("https://api.example.com")
};
}
[ClassCleanup]
public static void ClassCleanup()
{
// Runs after all tests in this class complete
_client?.Dispose();
}
[TestMethod]
public async Task GetUsers_ReturnsSuccess()
{
var response = await _client!.GetAsync("/users");
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Requisitos
- Los métodos deben ser
public static - Tipo de valor devuelto:
void,TaskoValueTask(MSTest v3.3+) -
ClassInitializerequiere unTestContextparámetro -
ClassCleanupacepta cero parámetros o unTestContextparámetro (MSTest 3.8+) - Solo uno de cada atributo permitido por clase
Comportamiento de herencia
Controlar si ClassInitialize se ejecuta para clases derivadas mediante InheritanceBehavior:
[TestClass]
public class BaseTestClass
{
[ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)]
public static void BaseClassInit(TestContext context)
{
// Runs before each derived class's tests
}
}
[TestClass]
public class DerivedTestClass : BaseTestClass
{
[TestMethod]
public void DerivedTest()
{
// BaseClassInit runs before this class's tests
}
}
| InheritanceBehavior | Description |
|---|---|
None (valor predeterminado) |
Inicializar solo se ejecuta para la clase declarante. |
BeforeEachDerivedClass |
Inicializar ejecuciones antes de cada clase derivada |
Sugerencia
Analizadores relacionados:
-
MSTEST0010 : valida la
ClassInitializefirma. -
MSTEST0011 : valida la
ClassCleanupfirma. -
MSTEST0034 : recomienda usar
ClassCleanupBehavior.EndOfClass.
Ciclo de vida de nivel de prueba global
Nota:
Los atributos de ciclo de vida de pruebas globales se introdujeron en MSTest 3.10.0.
Los métodos de ciclo de vida de pruebas globales se ejecutan antes y después de cada método de prueba en todo el ensamblado, sin necesidad de agregar código a cada clase de prueba.
GlobalTestInitialize y GlobalTestCleanup
[TestClass]
public class GlobalTestLifecycleExample
{
[GlobalTestInitialize]
public static void GlobalTestInit(TestContext context)
{
// Runs before every test method in the assembly
context.WriteLine($"Starting test: {context.TestName}");
}
[GlobalTestCleanup]
public static void GlobalTestCleanup(TestContext context)
{
// Runs after every test method in the assembly
context.WriteLine($"Finished test: {context.TestName}");
}
}
Requisitos
- Los métodos deben ser
public static - Tipo de valor devuelto:
void,TaskoValueTask - Debe tener exactamente un
TestContextparámetro - Debe estar en una clase marcada con
[TestClass] - Se permiten varios métodos con estos atributos en el ensamblado
Nota:
Cuando existen varios GlobalTestInitialize métodos o GlobalTestCleanup , no se garantiza el orden de ejecución. No se admite TimeoutAttribute en métodos GlobalTestInitialize.
Sugerencia
Analizador relacionado: MSTEST0050 : valida métodos globales de accesorios de prueba.
Ciclo de vida de nivel de prueba
El ciclo de vida a nivel de prueba se ejecuta para cada método de prueba. Para las pruebas con parámetros, el ciclo de vida se ejecuta para cada fila de datos.
Fase de configuración
Utilice TestInitialize o un constructor para la configuración para cada prueba.
[TestClass]
public class TestLevelSetupExample
{
private Calculator? _calculator;
public TestLevelSetupExample()
{
// Constructor runs before TestInitialize
// Use for simple synchronous initialization
}
[TestInitialize]
public async Task TestInit()
{
// Runs before each test method
// Supports async, attributes like Timeout
_calculator = new Calculator();
await _calculator.InitializeAsync();
}
[TestMethod]
public void Add_TwoNumbers_ReturnsSum()
{
var result = _calculator!.Add(2, 3);
Assert.AreEqual(5, result);
}
}
Constructor frente a TestInitialize:
| Aspecto | Constructor | TestInitialize |
|---|---|---|
| Compatibilidad asincrónica | No | Sí |
| Soporte de timeout | No | Sí (con [Timeout] atributo) |
| Orden de ejecución | Primero | Después del constructor |
| Herencia | Base a continuación derivada | Base a continuación derivada |
| Comportamiento de excepción | Las funciones de limpieza y eliminación no se ejecutan (no existe ninguna instancia) | Limpieza y eliminación se siguen ejecutando |
Sugerencia
¿Qué enfoque debo usar? Los constructores suelen ser preferidos porque permiten usar readonly campos, lo que impone inmutabilidad y hace que la clase de prueba sea más comprensible. Use TestInitialize cuando necesite compatibilidad con la inicialización asincrónica o el tiempo de espera.
También puede combinar ambos enfoques: use el constructor para la inicialización sincrónica simple de readonly campos y TestInitialize para una configuración asincrónica adicional que dependa de esos campos.
Opcionalmente, puede habilitar analizadores de código para aplicar un enfoque coherente:
- MSTEST0019: preferir métodos TestInitialize sobre constructores
- MSTEST0020 : se prefieren constructores sobre los métodos TestInitialize
Fase de ejecución
El método de prueba se ejecuta una vez completada la instalación. Para los métodos de prueba async, MSTest espera el retorno de Task o ValueTask.
Advertencia
Los métodos de prueba asincrónicos no tienen un SynchronizationContext por defecto. Esto no se aplica a las pruebas de UITestMethod en UWP y WinUI, que se ejecutan en el hilo de la interfaz de usuario.
Fase de limpieza
Utilice TestCleanup o IDisposable/IAsyncDisposable para limpiar después de cada prueba:
[TestClass]
public class TestLevelCleanupExample
{
private HttpClient? _client;
[TestInitialize]
public void TestInit()
{
_client = new HttpClient();
}
[TestCleanup]
public void TestCleanup()
{
if (_client != null)
{
_client.Dispose();
}
}
[TestMethod]
public async Task GetData_ReturnsSuccess()
{
var response = await _client!.GetAsync("https://example.com");
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Orden de ejecución de limpieza (derivado a base):
-
TestCleanup(clase derivada) -
TestCleanup(clase base) -
DisposeAsync(si se implementa) -
Dispose(si se implementa)
Sugerencia
Opcionalmente, puede habilitar analizadores de código para aplicar un enfoque de limpieza coherente:
- MSTEST0021 - Prefiera Dispose sobre los métodos TestCleanup
- MSTEST0022 : preferir TestCleanup sobre los métodos Dispose
Si tiene habilitados analizadores que no son de MSTest, como reglas de análisis de código de .NET, es posible que vea CA1001 sugiriendo que implemente el patrón de eliminación cuando la clase de prueba posee recursos desechables. Este es el comportamiento esperado y debe seguir las instrucciones del analizador.
Completar el orden de nivel de prueba
- Creación de una instancia de la clase de prueba (constructor)
- Establecer la propiedad
TestContext(si está presente) - Ejecutar métodos
GlobalTestInitialize - Ejecutar métodos
TestInitialize(de base a derivado) - Ejecutar método de prueba
- Actualiza
TestContextcon los resultados (por ejemplo, la propiedadOutcome) - Ejecutar
TestCleanupmétodos (derivados a base) - Ejecuta métodos
GlobalTestCleanup - Ejecutar
DisposeAsync(si está implementado) - Ejecutar
Dispose(si está implementado)
Sugerencia
Analizadores relacionados:
-
MSTEST0008: valida la
TestInitializefirma. -
MSTEST0009: valida la
TestCleanupfirma. - MSTEST0063: valida el constructor de clase de prueba.
procedimientos recomendados
Usar el ámbito adecuado: coloque la configuración en el nivel más alto que tenga sentido para evitar el trabajo redundante.
Mantener la configuración rápida: la configuración prolongada afecta a todas las pruebas. Considere la posibilidad de inicialización diferida para recursos costosos.
Limpiar correctamente: limpie siempre los recursos para evitar fugas de memoria y interferencias de prueba.
Manejar correctamente async: use
async Tasktipos de retorno, noasync void, para métodos de ciclo de vida asincrónicos.Considere el aislamiento de las pruebas: cada prueba debe ser independiente. Evite el estado mutable compartido entre las pruebas.
Use GlobalTest con moderación: los métodos de ciclo de vida global se ejecutan para cada prueba, por lo que deben mantenerse ligeros.