Testing unità di C# in .NET usando il test dotnet e xUnit

Questa esercitazione illustra come compilare una soluzione contenente un progetto di unit test e un progetto di codice sorgente. Per seguire l'esercitazione usando una soluzione predefinita, visualizzare o scaricare il codice di esempio. Per istruzioni sul download, vedere Esempi ed esercitazioni.

Creare la soluzione

In questa sezione viene creata una soluzione che contiene i progetti di origine e test. La soluzione completata ha la struttura di directory seguente:

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

Le istruzioni seguenti forniscono i passaggi per creare la soluzione di test. Per istruzioni su come creare la soluzione di test in un unico passaggio, vedere Comandi per creare una soluzione di test.

  • Aprire una finestra della shell.

  • Esegui questo comando:

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

    Il comando dotnet new sln crea una nuova soluzione nella directory unit-testing-using-dotnet-test.

  • Passare alla cartella unit-testing-using-dotnet-test.

  • Esegui questo comando:

    dotnet new classlib -o PrimeService
    

    Il comando dotnet new classlib crea un nuovo progetto di libreria di classi nella cartella PrimeService. La nuova libreria di classi conterrà il codice da testare.

  • Assegnare il nome PrimeService.cs al file Class1.cs.

  • Sostituire il codice in PrimeService.cs con il codice seguente:

    using System;
    
    namespace Prime.Services
    {
        public class PrimeService
        {
            public bool IsPrime(int candidate)
            {
                throw new NotImplementedException("Not implemented.");
            }
        }
    }
    
  • Il codice precedente:

    • Genera un oggetto NotImplementedException con un messaggio che indica che non è implementato.
    • Viene aggiornato più avanti nell'esercitazione.
  • Nella directory unit-testing-using-dotnet-test eseguire il comando seguente per aggiungere il progetto di libreria di classi alla soluzione:

    dotnet sln add ./PrimeService/PrimeService.csproj
    
  • Creare il progetto PrimeService.Tests eseguendo il comando seguente:

    dotnet new xunit -o PrimeService.Tests
    
  • Il comando precedente:

    • Crea il progetto PrimeService.Tests nella directory PrimeService.Tests. Il progetto di test usa xUnit come libreria di test.
    • Configura il test runner aggiungendo gli elementi <PackageReference /> seguenti al file di progetto:
      • Microsoft.NET.Test.Sdk
      • xunit
      • xunit.runner.visualstudio
      • coverlet.collector
  • Aggiungere il progetto di test al file della soluzione eseguendo il comando seguente:

    dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj
    
  • Aggiungere la libreria di classi PrimeService come dipendenza al progetto PrimeService.Tests:

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

Comandi per creare la soluzione

Questa sezione riepiloga tutti i comandi della sezione precedente. Ignorare questa sezione se sono stati completati i passaggi della sezione precedente.

I comandi seguenti creano la soluzione di test in un computer Windows. Per macOS e Unix, aggiornare il comando ren alla versione del sistema operativo di ren per rinominare un file:

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

Seguire le istruzioni per "Sostituire il codice in PrimeService.cs con il codice seguente" nella sezione precedente.

Creazione di un test

Un approccio comune nello sviluppo basato su test (TDD) consiste nello scrivere un test (non riuscito) prima di implementare il codice di destinazione. Questa esercitazione usa l'approccio TDD. Il metodo IsPrime è chiamabile, ma non implementato. Una chiamata di test a IsPrime ha esito negativo. Con TDD, viene scritto un test noto per avere esito negativo. Il codice di destinazione viene aggiornato in modo che il test abbia esito positivo. Continuare a ripetere questo approccio, scrivendo un test che ha esito negativo e quindi aggiornando il codice di destinazione in modo che abbia esito positivo.

Aggiornare il progetto PrimeService.Tests:

  • Eliminare PrimeService.Tests/UnitTest1.cs.
  • Creare un file PrimeService.Tests/PrimeService_IsPrimeShould.cs .
  • Sostituire il codice in PrimeService_IsPrimeShould.cs con il codice seguente:
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");
        }
    }
}

L'attributo [Fact] dichiara un metodo di test eseguito dal test runner. Dalla cartella PrimeService.Tests eseguire dotnet test. Il comando dotnet test compila entrambi i progetti ed esegue i test. Il test runner di xUnit include il punto d'ingresso del programma per l'esecuzione dei test. dotnet test avvia il test runner usando il progetto di unit test.

Il test ha esito negativo perché IsPrime non è stato implementato. Usando l'approccio TDD, scrivere solo codice sufficiente per l'esito positivo di questo test. Aggiornare IsPrime con il codice seguente:

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

Eseguire dotnet test. Il test viene superato.

Aggiungere altri test

Aggiungere test dei numeri primi per 0 e -1. È possibile copiare il test creato nel passaggio precedente e creare copie del codice seguente per testare 0 e -1. Ma meglio non farlo, perché c'è un modo migliore.

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

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

La copia del codice di test con cambiamento di un solo parametro comporta la duplicazione del codice e un incremento eccessivo dei test. Gli attributi di xUnit seguenti consentono di scrivere un gruppo di test simili:

  • [Theory] rappresenta una suite di test che eseguono lo stesso codice, ma hanno argomenti di input diversi.
  • L'attributo [InlineData] specifica i valori per tali input.

Anziché creare nuovi test, applicare gli attributi di xUnit precedenti per creare una singola teoria. Sostituire il codice seguente:

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

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

con il seguente codice:

[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");
}

Nel codice precedente, [Theory] e [InlineData] abilitano il test di diversi valori minori di due. Due è il numero primo più piccolo.

Aggiungere il codice seguente dopo la dichiarazione di classe e prima dell'attributo [Theory]:

private readonly PrimeService _primeService;

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

Eseguire dotnet test e due dei test hanno esito negativo. Per fare in modo che tutti i test abbiano un esito positivo, aggiornare il metodo IsPrime con il codice seguente:

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

Seguendo l'approccio TDD, aggiungere altri test con esito negativo, quindi aggiornare il codice di destinazione. Vedere la versione completata dei test e l'implementazione completa della libreria.

Il metodo IsPrime completato non è un algoritmo efficiente per il test della primalità.

Risorse aggiuntive