Prueba unitaria de C# en .NET mediante pruebas de dotnet y xUnit

En este tutorial se muestra cómo compilar una solución que contiene un proyecto de prueba unitaria y un proyecto de código fuente. Para seguir el tutorial con una solución precompilada, vea o descargue el código de ejemplo. Para obtener instrucciones de descarga, vea Ejemplos y tutoriales.

Creación de la solución

En esta sección, se crea una solución que contiene los proyectos de prueba y origen. La solución completada tiene la siguiente estructura de directorios:

/unit-testing-using-dotnet-test
    unit-testing-using-dotnet-test.sln
    /PrimeService
        PrimeService.cs
        PrimeService.csproj
    /PrimeService.Tests
        PrimeService_IsPrimeShould.cs
        PrimeServiceTests.csproj

Las instrucciones siguientes proporcionan los pasos para crear la solución de prueba. Consulte la sección sobre comandos para crear la solución de prueba a fin de obtener instrucciones para crear la solución de prueba en un paso.

  • Abra una ventana del Shell.

  • Ejecute el siguiente comando:

    dotnet new sln -o unit-testing-using-dotnet-test
    

    El comando dotnet new sln crea una nueva solución en el directorio unit-testing-using-dotnet-test.

  • Cambie el directorio a la carpeta unit-testing-using-dotnet-test.

  • Ejecute el siguiente comando:

    dotnet new classlib -o PrimeService
    

    El comando dotnet new classlib crea un nuevo proyecto de biblioteca de clases en la carpeta PrimeService. La nueva biblioteca de clases contendrá el código que se va a probar.

  • Cambie el nombre de Class1.cs a PrimeService.cs.

  • Reemplace el código de PrimeService.cs por el código siguiente:

    using System;
    
    namespace Prime.Services
    {
        public class PrimeService
        {
            public bool IsPrime(int candidate)
            {
                throw new NotImplementedException("Not implemented.");
            }
        }
    }
    
  • El código anterior:

    • Produce una excepción NotImplementedException con un mensaje que indica que no se ha implementado.
    • Se actualiza más adelante en el tutorial.
  • En el directorio unit-testing-using-dotnet-test, ejecute el comando siguiente para agregar el proyecto de biblioteca de clases a la solución:

    dotnet sln add ./PrimeService/PrimeService.csproj
    
  • Cree el proyecto PrimeService.Tests ejecutando el comando siguiente:

    dotnet new xunit -o PrimeService.Tests
    
  • El comando anterior:

    • Crea el proyecto PrimeService.Tests en el directorio PrimeService.Tests. El proyecto de prueba usa xUnit como biblioteca de pruebas.
    • Configura el ejecutor de pruebas agregando los siguientes <PackageReference />elementos al archivo del proyecto:
      • Microsoft.NET.Test.Sdk
      • xunit
      • xunit.runner.visualstudio
      • coverlet.collector
  • Agregue el proyecto de prueba al archivo de solución ejecutando el siguiente comando:

    dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj
    
  • Agregue la biblioteca de clases PrimeService como dependencia al proyecto PrimeService.Tests:

    dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj  
    

Comandos para crear la solución

En esta sección se resumen todos los comandos de la sección anterior. Omita esta sección si ha completado los pasos en la sección anterior.

Los comandos siguientes crean la solución de prueba en una máquina Windows. Para macOS y Unix, actualice el comando ren a la versión del SO de ren para cambiar el nombre de un archivo:

dotnet new sln -o unit-testing-using-dotnet-test
cd unit-testing-using-dotnet-test
dotnet new classlib -o PrimeService
ren .\PrimeService\Class1.cs PrimeService.cs
dotnet sln add ./PrimeService/PrimeService.csproj
dotnet new xunit -o PrimeService.Tests
dotnet add ./PrimeService.Tests/PrimeService.Tests.csproj reference ./PrimeService/PrimeService.csproj
dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj

Siga las instrucciones de "Reemplace el código de PrimeService.cs por el siguiente código" de la sección anterior.

Creación de una prueba

Un enfoque popular en el desarrollo controlado por pruebas (TDD) consiste en escribir una prueba (con errores) antes de implementar el código de destino. En este tutorial se usa el enfoque TDD. Se puede llamar al método IsPrime, pero sin implementar. Se produce un error en una llamada de prueba a IsPrime. Con TDD, se escribe una prueba a sabiendas de que en esta se produce un error. El código de destino se actualiza para conseguir que se supere la prueba. Siga repitiendo este enfoque, escribiendo una prueba con errores y, a continuación, actualizando el código de destino que se va a pasar.

Actualice el proyecto PrimeService.Tests:

  • Elimine PrimeService.Tests/UnitTest1.cs.
  • Cree un archivo PrimeService.Tests/PrimeService_IsPrimeShould.cs.
  • Reemplace el código de PrimeService_IsPrimeShould.cs por el código siguiente:
using Xunit;
using Prime.Services;

namespace Prime.UnitTests.Services
{
    public class PrimeService_IsPrimeShould
    {
        [Fact]
        public void IsPrime_InputIs1_ReturnFalse()
        {
            var primeService = new PrimeService();
            bool result = primeService.IsPrime(1);

            Assert.False(result, "1 should not be prime");
        }
    }
}

El atributo [Fact] declara un método de prueba que el ejecutor de pruebas ejecuta. En la carpeta PrimeService.Tests, ejecute dotnet test. El comando dotnet test compila ambos proyectos y ejecuta las pruebas. El ejecutor de pruebas de xUnit tiene el punto de entrada del programa para ejecutar las pruebas. dotnet test inicia el ejecutor de pruebas con el proyecto de prueba unitaria.

Se produce un error en la prueba porque no se ha implementado IsPrime. Con el enfoque TDD, solo tiene que escribir código suficiente para que se supere esta prueba. Actualice IsPrime con el siguiente código:

public bool IsPrime(int candidate)
{
    if (candidate == 1)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

Ejecute dotnet test. La prueba se supera.

Incorporación de más pruebas

Agregue pruebas de números primos para 0 y -1. Puede copiar la prueba creada en el paso anterior y hacer copias del código siguiente para probar 0 y -1. Pero no lo haga, ya que hay una mejor manera.

var primeService = new PrimeService();
bool result = primeService.IsPrime(1);

Assert.False(result, "1 should not be prime");

La copia de código de prueba cuando solamente es un parámetro el que cambia da lugar a la duplicación de código y al sobredimensionamiento de pruebas. Los siguientes atributos de xUnit habilitan la escritura de un conjunto de pruebas similares:

  • Un elemento [Theory] representa un conjunto de pruebas que ejecutan el mismo código, pero tienen diferentes argumentos de entrada.
  • Un atributo [InlineData] especifica valores para esas entradas.

En lugar de crear pruebas nuevas, aplique los atributos de xUnit anteriores para crear una sola teoría. Reemplace el código siguiente:

[Fact]
public void IsPrime_InputIs1_ReturnFalse()
{
    var primeService = new PrimeService();
    bool result = primeService.IsPrime(1);

    Assert.False(result, "1 should not be prime");
}

por el siguiente código:

[Theory]
[InlineData(-1)]
[InlineData(0)]
[InlineData(1)]
public void IsPrime_ValuesLessThan2_ReturnFalse(int value)
{
    var result = _primeService.IsPrime(value);

    Assert.False(result, $"{value} should not be prime");
}

En el código anterior, [Theory] y [InlineData] habilitan la prueba de varios valores inferiores a dos. Dos es el número primo más pequeño.

Agregue el código siguiente después de la declaración de clase y antes del atributo [Theory]:

private readonly PrimeService _primeService;

public PrimeService_IsPrimeShould()
{
    _primeService = new PrimeService();
}

Ejecute dotnet test, y dos de las pruebas no se superarán. Para que se superen todas las pruebas, actualice el método IsPrime con el siguiente código:

public bool IsPrime(int candidate)
{
    if (candidate < 2)
    {
        return false;
    }
    throw new NotImplementedException("Not fully implemented.");
}

Siguiendo el enfoque TDD, agregue más pruebas con errores y, a continuación, actualice el código de destino. Consulte la versión terminada de las pruebas y la implementación completa de la biblioteca.

El método IsPrime completado no es un algoritmo eficaz para probar los números primos.

Recursos adicionales