Cargando...

Escribir pruebas unitarias en C# (xUnit, NUnit, MSTest)

Aprende a escribir pruebas unitarias en C# con xUnit, NUnit y MSTest para crear aplicaciones más confiables.

En el desarrollo de software, las pruebas unitarias (unit tests) garantizan que las partes más pequeñas de una aplicación (métodos, clases o funciones) funcionen correctamente de forma aislada. En C#, los marcos de prueba más utilizados son xUnit, NUnit y MSTest. Las pruebas unitarias ayudan a detectar errores en etapas tempranas, mejorar la calidad del código y permitir un refactoring seguro.


¿Qué es una prueba unitaria?

Las pruebas unitarias verifican si un método produce la salida esperada para entradas determinadas. Generalmente, las pruebas se aíslan por escenario y se separan de las dependencias externas (base de datos, sistema de archivos, acceso a red).


// Ejemplo: método a probar
public class Calculadora
{
    public int Sumar(int a, int b) => a + b;
}

En el ejemplo anterior, el método Sumar es la unidad que debe probarse.


Crear un proyecto de prueba

Puedes crear fácilmente un proyecto de prueba con Visual Studio o la CLI de .NET:


// Crear un proyecto de prueba con xUnit
dotnet new xunit -n Calculadora.Tests
cd Calculadora.Tests

// Agregar referencia al proyecto principal
dotnet add reference ../Calculadora/Calculadora.csproj

Alternativamente, puedes usar las plantillas nunit o mstest:


dotnet new nunit -n NombreProyecto.Tests
dotnet new mstest -n NombreProyecto.Tests

Escribir pruebas con xUnit

xUnit es un marco de pruebas moderno y flexible ampliamente utilizado en el ecosistema de C#. El atributo [Fact] define pruebas individuales, mientras que [Theory] se usa para pruebas parametrizadas.


using Xunit;

public class CalculadoraTests
{
    [Fact]
    public void Sumar_DebeDevolverResultadoCorrecto()
    {
        // Arrange
        var c = new Calculadora();

        // Act
        int resultado = c.Sumar(2, 3);

        // Assert
        Assert.Equal(5, resultado);
    }

    [Theory]
    [InlineData(1, 2, 3)]
    [InlineData(-1, 5, 4)]
    [InlineData(10, -2, 8)]
    public void Sumar_ConValoresDiferentes_DebeFuncionarCorrectamente(int a, int b, int esperado)
    {
        var c = new Calculadora();
        Assert.Equal(esperado, c.Sumar(a, b));
    }
}

Fact: prueba un solo escenario. Theory: prueba el mismo método con varios conjuntos de datos.


Escribir pruebas con NUnit

NUnit es un marco de pruebas potente y de larga trayectoria en el mundo de C#. Es conocido por su simplicidad y su amplia variedad de aserciones.


using NUnit.Framework;

[TestFixture]
public class CalculadoraTests
{
    private Calculadora calc;

    [SetUp]
    public void Setup()
    {
        calc = new Calculadora();
    }

    [Test]
    public void Sumar_Prueba()
    {
        int resultado = calc.Sumar(4, 6);
        Assert.That(resultado, Is.EqualTo(10));
    }

    [TestCase(1, 2, 3)]
    [TestCase(5, 5, 10)]
    public void Sumar_ConValoresDiferentes_Prueba(int a, int b, int esperado)
    {
        Assert.That(calc.Sumar(a, b), Is.EqualTo(esperado));
    }
}

En NUnit, el método [SetUp] se ejecuta antes de cada prueba, y [TestCase] se utiliza para pruebas parametrizadas.


Escribir pruebas con MSTest

MSTest es el marco de pruebas integrado de Microsoft que viene con Visual Studio. Se utiliza con frecuencia en proyectos empresariales.


using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class CalculadoraTests
{
    private Calculadora calc;

    [TestInitialize]
    public void Init()
    {
        calc = new Calculadora();
    }

    [TestMethod]
    public void Sumar_Prueba()
    {
        int resultado = calc.Sumar(2, 8);
        Assert.AreEqual(10, resultado);
    }

    [DataTestMethod]
    [DataRow(1, 1, 2)]
    [DataRow(-3, 5, 2)]
    public void Sumar_ConValoresDiferentes_Prueba(int a, int b, int esperado)
    {
        Assert.AreEqual(esperado, calc.Sumar(a, b));
    }
}

En MSTest, [TestInitialize] se usa para la configuración antes de cada prueba, mientras que [DataTestMethod] se utiliza para pruebas basadas en datos.


Métodos de Aserción (Assert)

Todos los frameworks de pruebas incluyen estructuras de aserción similares:


// Ejemplo: prueba de excepción
[Fact]
public void Division_DebeLanzarDivideByZeroException()
{
    var calc = new Calculadora();
    Assert.Throws<DivideByZeroException>(() => calc.Dividir(5, 0));
}

Ejecución de Pruebas

Las pruebas se pueden ejecutar mediante la CLI o Visual Studio:


// Ejecutar pruebas con .NET CLI
dotnet test

En Visual Studio, la ventana “Test Explorer” permite ejecutar pruebas y visualizar los resultados exitosos o fallidos en una interfaz gráfica.


Mocking y Aislamiento de Dependencias

El mocking se utiliza para aislar las dependencias reales del entorno de prueba. Esto garantiza que las pruebas solo verifiquen el método objetivo y no se vean afectadas por factores externos (base de datos, servicios, etc.).


using Moq;
using Xunit;

public interface IServicioEmail
{
    void Enviar(string direccion, string mensaje);
}

public class RegistroUsuario
{
    private readonly IServicioEmail _email;
    public RegistroUsuario(IServicioEmail email) => _email = email;

    public void Registrar(string nombre)
    {
        _email.Enviar("admin@site.com", $"{nombre} ha sido registrado.");
    }
}

public class RegistroUsuarioTests
{
    [Fact]
    public void Registrar_DebeEnviarEmail()
    {
        var mock = new Mock<IServicioEmail>();
        var registro = new RegistroUsuario(mock.Object);

        registro.Registrar("Juan");

        mock.Verify(m => m.Enviar("admin@site.com", "Juan ha sido registrado."), Times.Once);
    }
}

La biblioteca Moq crea objetos simulados (mocks) para aislar el entorno de prueba.


Organización y Nomenclatura de Pruebas


Ejemplo Real: Prueba de Cálculo de Total de Pedido

El siguiente ejemplo prueba el cálculo del total de un pedido.


// Código de la aplicación
public class Pedido
{
    public List<decimal> PreciosProductos { get; set; } = new();
    public decimal Total() => PreciosProductos.Sum();
}

// Prueba
public class PedidoTests
{
    [Fact]
    public void Total_DebeDevolverElResultadoCorrecto()
    {
        var p = new Pedido();
        p.PreciosProductos.AddRange(new[] { 10m, 20m, 30m });
        Assert.Equal(60m, p.Total());
    }
}

Este es un ejemplo básico de prueba unitaria que garantiza que la lógica de negocio funcione correctamente.


Rendimiento y Mejores Prácticas


TL;DR

  • Prueba Unitaria: Prueba unidades de código pequeñas e independientes.
  • xUnit: Framework moderno, minimalista y ampliamente utilizado en .NET.
  • NUnit: Framework clásico con potentes aserciones y enfoque basado en atributos.
  • MSTest: Solución integrada de Microsoft, completamente compatible con Visual Studio.
  • Mocking: Aísla dependencias externas mediante objetos simulados.
  • Modelo AAA: Siga el patrón Arrange–Act–Assert.
  • El comando dotnet test ejecuta todas las pruebas desde la CLI.

Artículos relacionados