Testing unità di C# con MSTest e .NET

In questa esercitazione viene illustrata un'esperienza interattiva di compilazione passo passo di una soluzione di esempio finalizzata all'apprendimento dei concetti base del testing unità. Se si preferisce seguire l'esercitazione usando una soluzione preesistente, visualizzare o scaricare il codice di esempio prima di iniziare. Per istruzioni sul download, vedere Esempi ed esercitazioni.

Questo articolo è relativo al test di un progetto .NET Core. Se si sta testando un progetto ASP.NET Core, vedere Test di integrazione in ASP.NET Core.

Prerequisiti

Creare il progetto di origine

Aprire una finestra della shell. Creare una directory denominata unit-testing-using-mstest in cui archiviare la soluzione. In questa nuova directory, eseguire dotnet new sln per creare un nuovo file di soluzione per la libreria di classi e il progetto di test. Creare una directory PrimeService. Finora è stata creata la struttura di directory e file seguente:

/unit-testing-using-mstest
    unit-testing-using-mstest.sln
    /PrimeService

Impostare PrimeService come directory corrente ed eseguire dotnet new classlib per creare il progetto di origine. Assegnare il nome PrimeService.cs al file Class1.cs. Sostituire il codice nel file con il codice seguente per creare un'implementazione non riuscita della classe PrimeService:

using System;

namespace Prime.Services
{
    public class PrimeService
    {
        public bool IsPrime(int candidate)
        {
            throw new NotImplementedException("Please create a test first.");
        }
    }
}

Tornare alla directory unit-testing-using-mstest. Eseguire dotnet sln add per aggiungere il progetto di libreria di classi alla soluzione:

dotnet sln add PrimeService/PrimeService.csproj

Creare il progetto di test

Creare quindi la directory PrimeService.Tests. Di seguito è illustrata la struttura di directory:

/unit-testing-using-mstest
    unit-testing-using-mstest.sln
    /PrimeService
        Source Files
        PrimeService.csproj
    /PrimeService.Tests

Impostare PrimeService.Tests come directory corrente e creare un nuovo progetto usando dotnet new mstest. Il comando dotnet new crea un progetto di test che usa MSTest come libreria di test. Il modello configura il test runner nel file PrimeServiceTests.csproj:

<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
  <PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
  <PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
  <PackageReference Include="coverlet.collector" Version="1.3.0" />
</ItemGroup>

Per creare ed eseguire unit test, il progetto di test richiede altri pacchetti. dotnet new nel passaggio precedente è stato aggiunto MSTest SDK, il framework di test MSTest, lo strumento di esecuzione MSTest e il coverlet per la creazione di report di code coverage.

Aggiungere la libreria di classi PrimeService come altra dipendenza al progetto. Usare il comando dotnet add reference:

dotnet add reference ../PrimeService/PrimeService.csproj

È possibile visualizzare l'intero file nel repository degli esempi su GitHub.

Il layout della soluzione finale è il seguente:

/unit-testing-using-mstest
    unit-testing-using-mstest.sln
    /PrimeService
        Source Files
        PrimeService.csproj
    /PrimeService.Tests
        Test Source Files
        PrimeServiceTests.csproj

Passare alla directory unit-testing-using-mstest ed eseguire dotnet sln add:

dotnet sln add ./PrimeService.Tests/PrimeService.Tests.csproj

Creare il primo test

Scrivere un test con esito negativo, passarlo, quindi ripetere il processo. Rimuovere UnitTest1.cs dalla directory PrimeService.Tests e creare un nuovo file C# denominato PrimeService_IsPrimeShould.cs con il contenuto seguente:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Prime.Services;

namespace Prime.UnitTests.Services
{
    [TestClass]
    public class PrimeService_IsPrimeShould
    {
        private readonly PrimeService _primeService;

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

        [TestMethod]
        public void IsPrime_InputIs1_ReturnFalse()
        {
            bool result = _primeService.IsPrime(1);

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

L'attributo TestClass indica una classe che contiene unit test. L'attributo TestMethod indica che il metodo è un metodo di test.

Salvare questo file ed eseguire dotnet test per compilare i test e la libreria di classi, quindi eseguire i test. Il Test Runner di MSTest include il punto d'ingresso del programma per l'esecuzione dei test. dotnet test avvia il Test Runner usando il progetto di unit test creato.

Il test ha esito negativo. Non è stata ancora creata l'implementazione. Fare in modo che questo test venga superato scrivendo il codice più semplice e funzionante nella classe PrimeService:

public bool IsPrime(int candidate)
{
    if (candidate == 1)
    {
        return false;
    }
    throw new NotImplementedException("Please create a test first.");
}

Eseguire di nuovo dotnet test nella directory unit-testing-using-mstest. Il comando dotnet test esegue prima una compilazione del progetto PrimeService e quindi del progetto PrimeService.Tests. Dopo la compilazione di entrambi i progetti, verrà eseguito il test singolo, che viene superato.

Aggiungere altre funzionalità

Ora che il test è stato superato, è necessario scriverne altri. Esistono alcuni altri casi semplici per i numeri primi: 0, -1. È possibile aggiungere nuovi test con l'attributo TestMethod, ma questa operazione risulta rapidamente noiosa. Sono disponibili altri attributi di MSTest che consentono di scrivere una suite di test analoghi. Un metodo di test può eseguire lo stesso codice ma avere argomenti di input diversi. È possibile usare l'attributo DataRow per specificare i valori per tali input.

Anziché creare nuovi test, applicare questi due attributi per creare un singolo test basato sui dati. Il test basato sui dati è un metodo che testa diversi valori minori di due, ovvero il numero primo più basso. Aggiungere un nuovo metodo di test in PrimeService_IsPrimeShould.cs:

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

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

Eseguire dotnet test. Due test hanno esito negativo. Per assicurare che tutti i test vengano superati, modificare la clausola if all'inizio del metodo IsPrime nel file PrimeService.cs:

if (candidate < 2)

Continuare a eseguire l'iterazione aggiungendo altri test, altre teorie e altro codice nella libreria principale. Si ottiene la versione completa dei test e l'implementazione completa della libreria.

È stata compilata una piccola libreria e un set di unit test per tale libreria. La soluzione è stata strutturata in modo che l'aggiunta di nuovi pacchetti e test faccia parte del normale flusso di lavoro. La maggior parte del tempo e dell'impegno è dedicata alla soluzione degli obiettivi dell'applicazione.

Vedi anche