¿Qué hace csharp-xunit?
Obtén las mejores prácticas para pruebas unitarias con XUnit, incluyendo pruebas basadas en datos.
Visión General
Este skill te guía en la implementación de pruebas unitarias en C# con XUnit, cubriendo desde la configuración básica hasta patrones avanzados como pruebas basadas en datos, uso de mocks y buenas prácticas de organización.
Prerrequisitos
- Conocimiento básico de C# y .NET
- Proyecto con soporte para XUnit (paquete NuGet
xunit) - (Opcional) Paquete
xunit.runner.visualstudiopara integración con el IDE
Instalación
-
Agrega el paquete XUnit a tu proyecto:
dotnet add package xunit -
(Opcional) Para el ejecutor de pruebas desde la línea de comandos:
dotnet add package xunit.runner.visualstudio
Estructura de Prueba Recomendada
Sigue el patrón AAA (Arrange, Act, Assert) para cada prueba:
- Arrange: Configura las condiciones iniciales (objetos, datos de entrada, mocks).
- Act: Ejecuta la acción a probar (llamada al método).
- Assert: Verifica que el resultado sea el esperado.
Ejemplo Básico
using Xunit;
public class CalculadoraTests
{
[Fact]
public void Sumar_DosNumeros_DevuelveSuma()
{
// Arrange
var calc = new Calculadora();
int a = 2, b = 3;
// Act
var resultado = calc.Sumar(a, b);
// Assert
Assert.Equal(5, resultado);
}
}
Pruebas Basadas en Datos (Data-Driven Tests)
XUnit permite escribir una sola prueba que se ejecute con múltiples conjuntos de datos usando [Theory] y uno de los atributos de datos:
[InlineData]
Para datos simples y fijos:
[Theory]
[InlineData(1, 2, 3)]
[InlineData(-1, -1, -2)]
[InlineData(0, 0, 0)]
public void Sumar_VariosCasos_DevuelveResultadoEsperado(int a, int b, int esperado)
{
var calc = new Calculadora();
var resultado = calc.Sumar(a, b);
Assert.Equal(esperado, resultado);
}
[MemberData]
Para conjuntos de datos definidos en una propiedad o método estático:
public static IEnumerable<object[]> DatosSuma =>
new List<object[]>
{
new object[] { 1, 2, 3 },
new object[] { -1, -1, -2 },
new object[] { 0, 0, 0 }
};
[Theory]
[MemberData(nameof(DatosSuma))]
public void Sumar_DatosDesdeMember_DevuelveResultado(int a, int b, int esperado)
{
var calc = new Calculadora();
var resultado = calc.Sumar(a, b);
Assert.Equal(esperado, resultado);
}
[ClassData]
Para lógica de generación de datos más compleja, implementando IEnumerable<object[]>:
public class DatosCalculadora : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { 2, 3, 5 };
yield return new object[] { 10, -5, 5 };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
[Theory]
[ClassData(typeof(DatosCalculadora))]
public void Sumar_DatosDesdeClase_DevuelveResultado(int a, int b, int esperado)
{
var calc = new Calculadora();
var resultado = calc.Sumar(a, b);
Assert.Equal(esperado, resultado);
}
Uso de Fixtures Compartidas
Para compartir la configuración entre múltiples pruebas dentro de una clase, implementa IClassFixture<T>:
public class DatabaseFixture : IDisposable
{
public DatabaseFixture()
{
// Configuración de la base de datos (por ejemplo, en memoria)
Db = new List<string> { "dato1", "dato2" };
}
public List<string> Db { get; private set; }
public void Dispose()
{
// Limpieza
}
}
public class DatabaseTests : IClassFixture<DatabaseFixture>
{
private readonly DatabaseFixture _fixture;
public DatabaseTests(DatabaseFixture fixture)
{
_fixture = fixture;
}
[Fact]
public void ObtenerDatos_Existen_DevuelveLista()
{
var datos = _fixture.Db;
Assert.Contains("dato1", datos);
}
}
Mocks e Integración con Moq
Combina XUnit con Moq para aislar dependencias:
dotnet add package Moq
using Moq;
using Xunit;
public class ServicioNotificacionesTests
{
[Fact]
public void Enviar_ClienteValido_EnviaCorreo()
{
// Arrange
var mockCliente = new Mock<IClienteCorreo>();
var servicio = new ServicioNotificaciones(mockCliente.Object);
// Act
servicio.Enviar("[email protected]", "Asunto", "Cuerpo");
// Assert
mockCliente.Verify(c => c.EnviarCorreo(
"[email protected]", "Asunto", "Cuerpo"
), Times.Once);
}
}
Buenas Prácticas
-
Nombres descriptivos: Usa nombres de método que describan el escenario y el resultado esperado (ej.
Sumar_NumerosPositivos_DevuelveSuma). -
Una aserción por prueba (cuando sea posible): Facilita la localización de fallos.
-
Evita lógica en las pruebas: Las pruebas deben ser simples y predecibles.
-
Pruebas independientes: Cada prueba debe poder ejecutarse de forma aislada.
-
Usa
[Trait]para categorizar: Permite filtrar pruebas en CI/CD.[Fact] [Trait("Categoria", "Unitarias")] public void PruebaRapida() { }
Ejecución de Pruebas
Desde la terminal:
dotnet test
Para filtrar por categoría:
dotnet test --filter "Categoria=Unitarias"
Recursos Adicionales
Contribuciones
Si deseas contribuir a este skill, por favor revisa las guías de contribución en el repositorio original.