Procedura dettagliata: Creare ed eseguire unit test per il codice gestito
Questo articolo illustra come creare, eseguire e personalizzare una serie di unit test usando il framework di unit test Microsoft per il codice gestito e Visual Studio Esplora test. Si inizia con un progetto C# in fase di sviluppo, si creano test che ne eseguono il codice, eseguono i test ed esaminano i risultati. Modificare quindi il codice del progetto ed eseguire nuovamente i test. Per una panoramica concettuale di queste attività prima di eseguire questi passaggi, vedere nozioni di base di unit test. Per generare test automaticamente dal codice esistente, consulta Creazione di stub per metodi di test unitario dal codice.
Creare un progetto da testare
Apri Visual Studio.
Nella finestra iniziale scegliere Crea un nuovo progetto.
Cercare e selezionare il modello di progetto App console C# per .NET e quindi clic su Avanti.
Nota
Se non viene visualizzato il modello App Console, è possibile installarlo dalla finestra Crea un nuovo progetto. Nella messaggio Non trovi ciò che stai cercando?, scegli il collegamento Installa altri strumenti e funzionalità. Quindi, nel programma di installazione di Visual Studio, scegli il workload sviluppo di applicazioni desktop .NET.
Denominare il progetto Banke quindi fare clic su Avanti.
Scegliere il framework di destinazione consigliato o .NET 8 e quindi cliccare su Crea.
Il progetto Bank viene creato e visualizzato in Esplora soluzioni con il file Program.cs aperto nell'editor di codice.
Nota
Se Program.cs non è aperto nell'editor, fare doppio clic sul file Program.cs in Esplora soluzioni per aprirlo.
Sostituire il contenuto di Program.cs con il codice C# seguente che definisce una classe BankAccount:
using System; namespace BankAccountNS { /// <summary> /// Bank account demo class. /// </summary> public class BankAccount { private readonly string m_customerName; private double m_balance; private BankAccount() { } public BankAccount(string customerName, double balance) { m_customerName = customerName; m_balance = balance; } public string CustomerName { get { return m_customerName; } } public double Balance { get { return m_balance; } } public void Debit(double amount) { if (amount > m_balance) { throw new ArgumentOutOfRangeException("amount"); } if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } m_balance += amount; // intentionally incorrect code } public void Credit(double amount) { if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } m_balance += amount; } public static void Main() { BankAccount ba = new BankAccount("Mr. Bryan Walton", 11.99); ba.Credit(5.77); ba.Debit(11.22); Console.WriteLine("Current balance is ${0}", ba.Balance); } } }
Rinominare il file in BankAccount.cs facendo clic con il pulsante destro del mouse e scegliendo Rinomina in Esplora soluzioni.
Nel menu Compila fare clic su Compila soluzione oppure premere CTRL + MAIUSC + B).
È ora disponibile un progetto con metodi che è possibile testare. In questo articolo i test si concentrano sul metodo Debit
. Il metodo Debit
viene chiamato quando i soldi vengono ritirati da un conto.
Creare un progetto di unit test
Nel menu file selezionare Aggiungi>Nuovo progetto.
Suggerimento
È anche possibile fare clic con il pulsante destro del mouse sulla soluzione in esplora soluzioni e scegliere Aggiungi>Nuovo progetto.
Digitare di test nella casella di ricerca, selezionare C# come linguaggio, quindi selezionare il progetto di test MSTest C# per .NET e quindi fare clic su Avanti.
Nota
In Visual Studio 2019 versione 16.9 il modello di progetto MSTest è Progetto Unit Test.
Denominare il progetto BankTests e fare clic su Avanti.
Scegliere il framework di destinazione consigliato o .NET 8 e quindi cliccare su Crea.
A partire da Visual Studio 2022 versione 17.10, è anche possibile selezionare un test runner. Per il test runner, è possibile scegliere VSTest o MSTest . Per altre informazioni sulla differenza tra i test runner, vedere confronto tra Microsoft.Testing.Platform e VSTest.
Il progetto BankTests viene aggiunto alla soluzione Bank.
Nel progetto BankTests aggiungere un riferimento al progetto Bank.
In Esplora soluzioni, selezionare Dipendenze nel progetto BankTests e quindi scegliere Aggiungi riferimento (o Aggiungi riferimento a progetto) dal menu di scelta rapida.
Nella finestra di dialogo Gestione riferimenti, espandere Progetti, selezionare Soluzionee quindi controllare l'elemento Bank.
Scegliere OK.
Creare la classe di test
Creare una classe di test per verificare la classe BankAccount
. È possibile usare il file UnitTest1.cs generato dal modello di progetto, ma assegnare al file e alla classe nomi più descrittivi.
Rinominare un file e una classe
Per rinominare il file, in Esplora soluzioni selezionare il file UnitTest1.cs nel progetto BankTests. Dal menu di scelta rapida scegliere Rinomina (o premere F2) e quindi rinominare il file in BankAccountTests.cs.
Per rinominare la classe, posizionare il cursore su
UnitTest1
nell'editor di codice, fare clic con il pulsante destro del mouse e quindi scegliere Rinomina (oppure premere F2). Digitare BankAccountTests e quindi premere INVIO.
Il file BankAccountTests.cs contiene ora il codice seguente:
// The 'using' statement for Test Tools is in GlobalUsings.cs
// using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BankTests
{
[TestClass]
public class BankAccountTests
{
[TestMethod]
public void TestMethod1()
{
}
}
}
Aggiungere un'istruzione using
Aggiungere un'istruzione using
alla classe di test per poter chiamare il progetto sottoposto a test senza usare nomi completi. Nella parte superiore del file di classe aggiungere:
using BankAccountNS;
Requisiti della classe di test
I requisiti minimi per una classe di test sono:
L'attributo
[TestClass]
è necessario in qualsiasi classe contenente i metodi di unit test da eseguire in Esplora test.Ogni metodo di test che si vuole riconoscere in Esplora test deve avere l'attributo
[TestMethod]
.
È possibile avere altre classi in un progetto di unit test che non hanno l'attributo [TestClass]
ed è possibile avere altri metodi nelle classi di test che non dispongono dell'attributo [TestMethod]
. È possibile chiamare queste altre classi e metodi dai metodi di test.
Creare il primo metodo di test
In questa procedura si scrivono metodi di unit test per verificare il comportamento del metodo Debit
della classe BankAccount
.
È necessario controllare almeno tre comportamenti:
Il metodo genera un ArgumentOutOfRangeException se l'importo del debito è maggiore del saldo.
Il metodo genera un ArgumentOutOfRangeException se l'importo del debito è minore di zero.
Se l'importo del debito è valido, il metodo sottrae l'importo di debito dal saldo del conto.
Suggerimento
È possibile eliminare il metodo di TestMethod1
predefinito, perché non verrà usato in questa procedura dettagliata.
Per creare un metodo di test
Il primo test verifica che un importo valido (ovvero uno minore del saldo del conto e maggiore di zero) ritiri l'importo corretto dal conto. Aggiungere il metodo seguente alla classe BankAccountTests
:
[TestMethod]
public void Debit_WithValidAmount_UpdatesBalance()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 4.55;
double expected = 7.44;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
account.Debit(debitAmount);
// Assert
double actual = account.Balance;
Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
}
Il metodo è semplice: configura un nuovo oggetto BankAccount
con un saldo iniziale e quindi ritira un importo valido. Usa il metodo Assert.AreEqual per verificare che il saldo finale sia come previsto. Metodi come Assert.AreEqual
, Assert.IsTruee altri vengono spesso usati negli unit test. Per altre informazioni concettuali sulla scrittura di un unit test, vedere Scrivere i tuoi test.
Requisiti del metodo di test
Un metodo di test deve soddisfare i requisiti seguenti:
È decorato con l'attributo
[TestMethod]
.Restituisce
void
.Non può avere parametri.
Compilare ed eseguire il test
Nel menu Compila scegliere Compila soluzione oppure premere CTRL + SHIFT + B.
Se Esplora test non è aperto, aprirlo scegliendo Test>Esplora test (o Test>Esplora test di Windows>) dalla barra dei menu superiore oppure premere CTRL + E, T).
Scegliere Esegui tutte le per eseguire il test oppure premere CTRL + R, V).
Durante l'esecuzione del test, la barra di stato nella parte superiore della finestra del Test Explorer è animata. Alla fine dell'esecuzione del test, la barra diventa verde se tutti i metodi di test superano o rosso se uno dei test ha esito negativo.
In questo caso, il test ha esito negativo.
Seleziona il metodo in Test Explorer per visualizzare i dettagli nella parte inferiore della finestra.
Correggere il codice ed eseguire di nuovo i test
Il risultato del test contiene un messaggio che descrive l'errore. Potrebbe essere necessario eseguire il drill-down per visualizzare questo messaggio. Per il metodo AreEqual
, il messaggio visualizza ciò che era previsto e ciò che è stato effettivamente ricevuto. Ci si aspettava che il saldo diminuisca, ma invece è aumentato dell'importo del ritiro.
Lo unit test ha scoperto un bug: l'importo del prelievo è aggiunto al saldo del conto quando deve essere sottratto.
Correggere il bug
Per correggere l'errore, nel file BankAccount.cs sostituire la riga:
m_balance += amount;
con:
m_balance -= amount;
Rieseguire il test
In Test Explorer, scegliere Esegui tutto per eseguire nuovamente il test (oppure premere Ctrl + R, V). La barra rossa/verde diventa verde per indicare che il test è stato superato.
superato
superato
Usare unit test per migliorare il codice
Questa sezione descrive come un processo iterativo di analisi, sviluppo di unit test e refactoring consente di rendere il codice di produzione più affidabile ed efficace.
Analizzare i problemi
È stato creato un metodo di test per verificare che un importo valido venga dedotto correttamente nel metodo Debit
. Verificare ora che il metodo generi un ArgumentOutOfRangeException se l'importo del debito è:
- maggiore del saldo, o
- minore di zero.
Creare ed eseguire nuovi metodi di test
Creare un metodo di test per verificare il comportamento corretto quando l'importo del debito è minore di zero:
[TestMethod]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = -100.00;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act and assert
Assert.ThrowsException<System.ArgumentOutOfRangeException>(() => account.Debit(debitAmount));
}
Utilizzare il metodo ThrowsException per asserire che è stata generata l'eccezione corretta. Questo metodo fa fallire il test a meno che non venga lanciato un ArgumentOutOfRangeException. Se si modifica temporaneamente il metodo sottoposto a test per generare un ApplicationException più generico quando l'importo del debito è minore di zero, il test si comporta correttamente, ovvero ha esito negativo.
Per verificare il caso quando l'importo ritirato è maggiore del saldo, seguire questa procedura:
Creare un nuovo metodo di test denominato
Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
.Copiare il corpo del metodo da
Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
al nuovo metodo.Imposta il
debitAmount
su un numero maggiore del saldo.
Eseguire i due test e verificare che vengano superati.
Continuare l'analisi
Il metodo sottoposto a test può essere migliorato ulteriormente. Con l'implementazione corrente, non è possibile sapere quale condizione (amount > m_balance
o amount < 0
) ha causato l'eccezione generata durante il test. Sappiamo solo che un ArgumentOutOfRangeException
è stato generato da qualche parte nel metodo . Sarebbe preferibile determinare quale condizione in BankAccount.Debit
ha causato la generazione dell'eccezione (amount > m_balance
o amount < 0
) in modo da essere sicuri che il nostro metodo stia verificando correttamente i suoi argomenti.
Esaminare di nuovo il metodo sottoposto a test (BankAccount.Debit
) e notare che entrambe le istruzioni condizionali usano un costruttore ArgumentOutOfRangeException
che accetta semplicemente il nome dell'argomento come parametro:
throw new ArgumentOutOfRangeException("amount");
È possibile usare un costruttore che segnala informazioni molto più complete: ArgumentOutOfRangeException(String, Object, String) include il nome dell'argomento, il valore dell'argomento e un messaggio definito dall'utente. È possibile effettuare il refactoring del metodo sottoposto a test per usare questo costruttore. Ancora meglio, è possibile usare membri di tipo disponibili pubblicamente per specificare gli errori.
Eseguire il refactoring del codice sottoposto a test
Prima di tutto, definire due costanti per i messaggi di errore nell'ambito della classe. Inserire le definizioni nella classe sottoposta a test BankAccount
:
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";
Modificare quindi le due istruzioni condizionali nel metodo Debit
:
if (amount > m_balance)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}
if (amount < 0)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
}
Eseguire il refactoring dei metodi di test
Effettuare il refactoring dei metodi di test rimuovendo la chiamata a Assert.ThrowsException. Incapsulare la chiamata a Debit()
in un blocco try/catch
, catturare l'eccezione specifica attesa e verificare il messaggio corrispondente. Il metodo Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains consente di confrontare due stringhe.
Il Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
potrebbe ora essere simile al seguente:
[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
try
{
account.Debit(debitAmount);
}
catch (System.ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
}
}
Riprovare, riscrivere e rianalizzare
Attualmente, il metodo di test non gestisce tutti i casi che dovrebbe. Se il metodo sottoposto a test, il metodo Debit
non è riuscito a generare un ArgumentOutOfRangeException quando il debitAmount
è maggiore del saldo (o minore di zero), il metodo di test passerà. Questo scenario non è valido perché si vuole che il metodo di test non riesca se non viene generata alcuna eccezione.
Questo risultato è un bug nel metodo di test. Per risolvere il problema, aggiungere un'asserzione Assert.Fail alla fine del metodo di test per gestire il caso in cui non viene generata alcuna eccezione.
Eseguendo nuovamente il test, si vede che il test ha ora esito negativo se viene rilevata l'eccezione corretta. Il blocco catch
intercetta l'eccezione, ma il metodo continua a essere eseguito e ha esito negativo nella nuova asserzione Assert.Fail. Per risolvere questo problema, aggiungere un'istruzione return
dopo il StringAssert
nel blocco catch
. Riesecuzione del test conferma che il problema è stato risolto. La versione finale del Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
è simile alla seguente:
[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
try
{
account.Debit(debitAmount);
}
catch (System.ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
return;
}
Assert.Fail("The expected exception was not thrown.");
}
Conclusione
I miglioramenti apportati al codice di test hanno portato a metodi di test più affidabili e informativi. Ma soprattutto, hanno migliorato anche il codice sottoposto a test.
Suggerimento
Questa procedura dettagliata usa il framework di unit test Microsoft per il codice gestito. Test Explorer può anche eseguire i test da framework di unit test di terze parti con adattatori per Test Explorer. Per ulteriori informazioni, consultare i framework di unit test di terze parti Install.
Contenuto correlato
Per informazioni su come eseguire test da una riga di comando, vedere VSTest.Console.exe opzioni della riga di comando.