Nozioni fondamentali sugli unit test
Questo argomento illustra le nozioni di base relative alla scrittura e all'esecuzione di unit test in Esplora test di Visual Studio.Include le sezioni seguenti:
Panoramica degli unit test
- Guide introduttive
Esempio della soluzione MyBank
Creazione dei progetti unit test
Scrittura dei test
Esecuzione di test in Esplora test
Esecuzione e visualizzazione di test dalla barra degli strumenti di Esplora test
Esecuzione di test dopo ogni compilazione
Applicazione di filtri e raggruppamento dell'elenco di test
Debug di unit test
Strumenti aggiuntivi per unit test
Generazione di codice dell'applicazione dai test
Generazione di più test tramite metodi di test basati sui dati
Analisi del code coverage di unit test
Isolamento di metodi di unit test tramite Microsoft Fakes
Panoramica degli unit test
Esplora test di Visual Studio è progettato per supportare gli sviluppatori e i team che incorporano unit test nelle procedure di sviluppo del software.Gli unit test semplificano la verifica della correttezza del programma, assicurando che il codice dell'applicazione esegua le operazioni previste.Gli unit test permettono di analizzare il funzionamento del programma per individuare comportamenti discreti testabili che possono essere testati come singole unità.Per creare test relativi a questi comportamenti e per segnalare i risultati di questi test, è possibile usare un framework per unit test.
Gli unit test raggiungono la massima efficacia quando sono parte integrante del flusso di lavoro dello sviluppo di software.Non appena si scrive una funzione o un altro blocco di codice dell'applicazione, si creano unit test per verificare il comportamento del codice in risposta a casi standard, limite e non corretti di dati di input e quindi per verificare eventuali ipotesi esplicite o implicite effettuate dal codice.In una procedura di sviluppo software nota come sviluppo basato sui test gli unit test vengono creati prima della scrittura di codice. Si usano quindi gli unit test come documentazione di progettazione e come specifiche funzionali della funzionalità.
Esplora test offre un modo flessibile ed efficiente per eseguire gli unit test e visualizzarne i risultati in Visual Studio.Visual Studio installa i framework per unit test Microsoft per codice gestito e nativo.Esplora test può eseguire anche framework per unit test di terze parti e open source che hanno implementato le interfacce dei componenti aggiuntivi di Esplora test.È possibile aggiungere molti di questi framework tramite Gestione estensioni di Visual Studio e la Visual Studio Gallery.Vedere Procedura: installare framework unit test di terze parti
Nelle visualizzazioni di Esplora test possono essere inclusi tutti i test oppure solo i test riusciti, non riusciti, non ancora eseguiti o ignorati.È possibile filtrare i test in qualsiasi visualizzazione tramite la corrispondenza con il testo nella casella di ricerca a livello globale oppure tramite la selezione di uno dei filtri predefiniti.È possibile eseguire qualsiasi selezione di test in qualsiasi momento.Quando si usa Visual Studio Ultimate, è possibile eseguire automaticamente i test dopo ogni compilazione.I risultati di un'esecuzione dei test sono visualizzati immediatamente nella barra relativa alle operazioni riuscite/non riuscite nella parte superiore della finestra di Esplora test.I dettagli relativi al risultato di un metodo di test vengono visualizzati quando si seleziona il test.
Guide introduttive
Per un'introduzione agli unit test che mostra direttamente la creazione di codice, vedere uno degli argomenti seguenti:
Procedura dettagliata: creazione ed esecuzione di unit test per codice gestito
Guida introduttiva allo sviluppo basato su test con Esplora test
Esempio della soluzione MyBank
In questo argomento si usa lo sviluppo di un'applicazione fittizia denominata MyBank come esempio.Per seguire le spiegazioni disponibili in questo argomento non è necessario il codice effettivo.I metodi di test sono scritti in C# e sono presentati tramite il framework per unit test Microsoft per codice gestito. I concetti possono essere tuttavia trasferiti facilmente ad altri linguaggi e altri framework.
Il primo tentativo di progettazione per l'applicazione MyBank include un componente conti che rappresenta un singolo conto e le rispettive transazioni con la banca e un componente database che rappresenta la funzionalità per l'aggregazione e la gestione dei singoli conti.
Verrà creata una soluzione MyBank che contiene due progetti:
Accounts
BankDb
Il primo tentativo di progettazione del progetto Accounts include una classe per le informazioni di base su un conto, un'interfaccia che specifica la funzionalità comune di un tipo di conto, ad esempio il deposito e il prelievo di beni dal conto, e una classe derivata dall'interfaccia che rappresenta un conto corrente.Il progetto Accounts inizia con la creazione dei file di origine seguenti:
AccountInfo.cs definisce le informazioni di base per un conto.
IAccount.cs definisce un'interfaccia IAccount standard per un conto, inclusi metodi per il deposito e il prelievo di beni da un conto e per il recupero del saldo del conto.
CheckingAccount.cs contiene la classe CheckingAccount che implementa l'interfaccia IAccounts per un conto corrente.
L'esperienza mostra che per un prelievo da un conto corrente è necessario essere sicuri che l'importo prelevato sia inferiore al saldo del conto.Viene quindi eseguito l'override del metodo IAccount.Withdaw in CheckingAccount con un metodo che verifica questa condizione.Il metodo può avere un aspetto analogo al seguente:
public void Withdraw(double amount)
{
if(m_balance >= amount)
{
m_balance -= amount;
}
else
{
throw new ArgumentException(amount, "Withdrawal exceeds balance!")
}
}
Ora che è disponibile codice, è possibile passare al test.
Creazione dei progetti unit test
Un progetto unit test rispecchia in genere la struttura di un progetto a codice singolo.Nell'esempio MyBank si aggiungono due progetti unit test denominati AccountsTests e BankDbTests alla soluzione MyBanks.I nomi dei progetti di test sono arbitrari, ma è consigliabile adottare una convenzione di denominazione standard.
Per aggiungere un progetto unit test a una soluzione:
Scegliere Nuovo dal menu File, quindi scegliere Progetto (CTRL + MAIUSC + N da tastiera).
Nella finestra di dialogo Nuovo progetto espandere il nodo Installato, scegliere il linguaggio da usare per il progetto di test, quindi scegliere Test.
Per usare uno dei framework per unit test Microsoft, scegliere Progetto unit test dall'elenco di modelli di progetto.In alternativa, scegliere il modello di progetto del framework per unit test che si vuole usare.Per testare il progetto Accounts dell'esempio, assegnare al progetto il nome AccountsTests.
Attenzione Non tutti i framework per unit test di terze parti e open source offrono un modello di progetto di Visual Studio.Per informazioni sulla creazione di un progetto, vedere la documentazione relativa al framework.
Nel progetto unit test aggiungere un riferimento al progetto di codice da testare, che nell'esempio corrisponde al progetto Accounts.
Per creare il riferimento al progetto di codice:
Selezionare il progetto in Esplora soluzioni.
Scegliere Aggiungi riferimento dal menu Progetto.
Nella finestra di dialogo Gestione riferimenti aprire il nodo Soluzione e scegliere Progetti.Selezionare il nome del progetto di codice e chiudere la finestra di dialogo.
Ogni progetto unit test contiene classi che rispecchiano i nomi delle classe del progetto di codice.Nell'esempio il progetto AccountsTests contiene le classi seguenti:
La classe AccountInfoTests contiene i metodi di unit test per la classe AccountInfo nel progetto BankAccount.
La classe CheckingAccountTests contiene i metodi di unit test per la classe CheckingAccount.
Scrittura dei test
Il framework per unit test usato e Visual Studio IntelliSense forniranno indicazioni per la creazione di unit test per un progetto di codice.Per l'esecuzione in Esplora test, la maggior parte dei framework richiede l'aggiunta di attributi specifici per identificare i metodi di unit test.I framework offrono anche un modo, in genere tramite istruzioni Assert o attributi di metodo, per indicare se un metodo di test ha avuto esito positivo o negativo.Altri attributi identificano metodi di configurazione facoltativi che si trovano in corrispondenza dell'inizializzazione delle classi e prima di ogni metodo di test e dei metodi di disinstallazione eseguiti dopo ogni metodo di test e prima dell'eliminazione della classe.
Lo schema AAA (Arrange, Act, Assert) è un modo comune per scrivere unit test per un metodo da testare.
La sezione Arrange di un metodo di unit test inizializza oggetti e imposta il valore dei dati passati al metodo da testare.
La sezione Act richiama il metodo da testare con i parametri definiti.
La sezione Assert verifica che l'azione del metodo da testare si comporti come previsto.
Per testare il metodo CheckingAccount.Withdraw dell'esempio, si possono scrivere due test: un test che verifica il comportamento standard del metodo e un test che verifica che un prelievo superiore al saldo del conto avrà esito negativo.Nella classe CheckingAccountTests vengono aggiunti i metodi seguenti:
[TestMethod]
public void Withdraw_ValidAmount_ChangesBalance()
{
// arrange
double currentBalance = 10.0;
double withdrawal = 1.0;
double expected = 9.0;
var account = new CheckingAccount("JohnDoe", currentBalance);
// act
account.Withdraw(withdrawal);
double actual = account.Balance;
// assert
Assert.AreEqual(expected, actual);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void Withdraw_AmountMoreThanBalance_Throws()
{
// arrange
var account = new CheckingAccount("John Doe", 10.0);
// act
account.Withdraw(1.0);
// assert is handled by the ExpectedException
}
Si noti che Withdraw_ValidAmount_ChangesBalance usa un'istruzione Assert esplicita per determinare se il metodo di test ha esito positivo o negativo, mentre Withdraw_AmountMoreThanBalance_Throws usa l'attributo ExpectedException per determinare la riuscita del metodo di test.In modo invisibile all'utente, un framework per unit test esegue il wrapping dei metodi di test in istruzioni try/catch.Nella maggior parte dei casi, se viene generata un'eccezione, il metodo di test avrà esito negativo e l'eccezione verrà ignorata.L'attributo ExpectedException provoca l'esito positivo del metodo di test se viene generata l'eccezione specificata.
Per altre informazioni sui framework per unit test Microsoft, vedere uno degli argomenti seguenti:
Scrittura di unit test per .NET Framework con il framework unit test di Microsoft per codice gestito
Scrittura di unit test per C/C++ con il framework unit test di Microsoft per C++.
Impostazione dei timeout
Per impostare un timeout in un singolo metodo di test:
[TestMethod]
[Timeout(2000)] // Milliseconds
public void My_Test()
{ ...
}
Per impostare il timeout sul valore massimo permesso:
[TestMethod]
[Timeout(TestTimeout.Infinite)] // Milliseconds
public void My_Test ()
{ ...
}
Esecuzione di test in Esplora test
Quando si compila il progetto di test, i test vengono visualizzati in Esplora test.Se Esplora test non è visualizzato, scegliere Test dal menu di Visual Studio, quindi scegliere Windows e infine Esplora test.
Durante l'esecuzione, la scrittura e la nuova esecuzione di test, la visualizzazione predefinita di Esplora test mostra i risultati in gruppi di Test non superati, Test superati, Test ignorati e Test non eseguiti.È possibile scegliere un'intestazione di gruppo per aprire la visualizzazione che mostra tutti i test disponibili nel gruppo.
Esecuzione e visualizzazione di test dalla barra degli strumenti di Esplora test
La barra degli strumenti di Esplora test permette di individuare, organizzare ed eseguire i test a cui si è interessati.
È possibile scegliere Esegui tutto per eseguire tutti i test oppure scegliere Esegui per selezionare un sottoinsieme di test da eseguire.Dopo l'esecuzione di un insieme di test, un riepilogo dei test verrà visualizzato nella parte inferiore della finestra Esplora test.Selezionare un test per visualizzarne i dettagli nel riquadro inferiore.Scegliere Apri Test dal menu di scelta rapida (tastiera: F12) per visualizzare il codice sorgente per il test selezionato.
Esecuzione di test dopo ogni compilazione
Attenzione |
---|
L'esecuzione di unit test dopo ogni compilazione è supportata solo in Visual Studio Ultimate. |
Per eseguire gli unit test dopo ogni compilazione locale, scegliere Test dal menu standard e quindi scegliere Esegui test dopo compilazione sulla barra degli strumenti di Esplora test. |
Applicazione di filtri e raggruppamento dell'elenco di test
Quando è disponibile un numero elevato di test, è possibile digitare nella casella di testo di Esplora test per filtrare l'elenco in base alla stringa specificata.È possibile limitare ulteriormente i risultati scegliendo uno dei filtri disponibili nell'elenco.
Per raggruppare i test in base alla categoria, scegliere il pulsante Raggruppa per. |
Per altre informazioni, vedere Esecuzione di unit test con Esplora test.
Debug di unit test
È possibile usare Esplora test per avviare una sessione di debug per i test.Esaminando con facilità il codice grazie al debugger di Visual Studio è possibile spostarsi in avanti e indietro tra gli unit test e i progetti da testare.Per avviare il debug:
Nell'editor di Visual Studio impostare un punto di interruzione in uno o più metodi di test di cui si vuole eseguire il debug.
[!NOTA]
Poiché i metodi di test possono essere eseguiti in qualsiasi ordine, impostare punti di interruzione in tutti i metodi di test di cui si vuole eseguire il debug.
In Esplora test selezionare i metodi di test e quindi scegliere Esegui debug test selezionati dal menu di scelta rapida.
Per altre informazioni sul debugger, vedere Debug in Visual Studio.
Strumenti aggiuntivi per unit test
Generazione di codice dell'applicazione dai test
Se si scrivono i test prima di scrivere il codice del progetto, sarà possibile usare IntelliSense per generare classi e metodi nel codice del progetto.Scrivere un'istruzione in un metodo di test che chiama la classe o il metodo da generare, quindi aprire il menu di IntelliSense sotto la chiamata.Se la chiamata è per un costruttore della nuova classe, scegliere Genera nuovo tipo dal menu, quindi eseguire la procedura guidata per inserire la classe nel progetto di codice.Se la chiamata è per un metodo, scegliere Genera nuovo metodo dal menu di IntelliSense.
Generazione di più test tramite metodi di test basati sui dati
[!NOTA]
Queste procedure sono applicabili soli ai metodi di test scritti usando il framework per unit test Microsoft per codice gestito.Se si usa un framework diverso, vedere la documentazione del framework per informazioni sulla funzionalità equivalente.
I metodi di test basati sui dati permettono di verificare un intervallo di valori in un singolo metodo di unit test.Per creare un metodo di unit test basato sui dati, decorare il metodo con un attributo DataSource che specifica un'origine dati e una tabella contenente i valori delle variabili da testare.Nel corpo del metodo assegnare i valori di riga alle variabili usando l'indicizzatore TestContext.DataRow[NomeColonna].
Ad esempio, si presupponga di aggiungere un metodo non necessario alla classe CheckingAccount con nome AddIntegerHelper.AddIntegerHelper aggiunge due valori Integer.
Per creare un test basato sui dati per il metodo AddIntegerHelper, è necessario creare prima di tutto un database di Access con nome AccountsTest.accdb e una tabella con nome AddIntegerHelperData.La tabella AddIntegerHelperData definisce colonne per specificare il primo e il secondo operando dell'addizione e una colonna per specificare il risultato previsto.I valori appropriati vengono inseriti in alcune righe.
[DataSource(
@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Projects\MyBank\TestData\AccountsTest.accdb",
"AddIntegerHelperData"
)]
[TestMethod()]
public void AddIntegerHelper_DataDrivenValues_AllShouldPass()
{
var target = new CheckingAccount();
int x = Convert.ToInt32(TestContext.DataRow["FirstNumber"]);
int y = Convert.ToInt32(TestContext.DataRow["SecondNumber"]);
int expected = Convert.ToInt32(TestContext.DataRow["Sum"]);
int actual = target.AddIntegerHelper(x, y);
Assert.AreEqual(expected, actual);
}
Il metodo con attributi viene eseguito una volta per ogni riga della tabella.In caso di esito negativo di una delle iterazioni, Esplora test segnala un errore di test per il metodo.Nel riquadro dei dettagli dei risultati del test per il metodo è mostrato il metodo di stato relativo all'esito positivo/negativo per ogni riga di dati.
Per altre informazioni, vedere Procedura: creare uno unit test basato sui dati.
Analisi del code coverage di unit test
[!NOTA]
Il code coverage per unit test è disponibile per linguaggi nativi e gestiti e tutti i framework di unit test che possono essere eseguiti dal framework per unit test.
È possibile determinare la quantità di codice del prodotto sottoposta effettivamente a test dagli unit test usando lo strumento per il code coverage di Visual Studio.È possibile eseguire il code coverage su test selezionati oppure su tutti i test in una soluzione.La finestra Risultati code coverage mostra la percentuale di blocchi di codice del prodotto esaminati in base a riga, funzione, classe, spazio dei nomi e modulo.
Per eseguire il code coverage per i metodi di test in una soluzione,
scegliere Test dal menu di Visual Studio, quindi scegliere Analizza code coverage.
Scegliere uno dei comandi seguenti:
Test selezionati esegue i metodi di test selezionati in Esplora test.
Tutti i test esegue tutti i metodi di test nella soluzione.
I risultati del code coverage sono visualizzati nella finestra Risultati code coverage.
Per altre informazioni, vedere Utilizzo di code coverage per determinare la quantità di codice testato.
Isolamento di metodi di unit test tramite Microsoft Fakes
[!NOTA]
Microsoft Fakes è disponibile solo in Visual Studio Ultimate.Microsoft Fakes può essere usato solo con i metodi di test scritti usando framework per unit test per codice gestito.
Il problema
I metodi di unit test specifici per la verifica del codice interno in una funzione possono essere difficili da scrivere quando il metodo da testare chiama funzioni che introducono dipendenze esterne.Ad esempio, i metodi della classe di esempio CheckingAccount dovrebbero probabilmente effettuare chiamate al componente BankDb per aggiornare il database principale.È possibile effettuare il refactoring della classe CheckingAccount in modo che abbia un aspetto analogo al seguente:
class CheckingAccount : IAccount
{
public CheckingAccount(customerName, double startingBalance, IBankDb bankDb)
{
m_bankDb = bankDb;
// set up account
}
public void Withdraw(double amount)
{
if(m_balance >= amount)
{
m_balance = m_MyBankDb.Withdraw(m_accountInfo.ID, amount);
}
else
{
throw new ArgumentException(amount, "Withdrawal exceeds balance!")
}
}
private IBankDb m_bankDb = null;
// ...
Gli unit test di questo metodo CheckingAccount.Withdraw possono ora avere esito negativo a causa di problemi provocati dalla chiamata a m_bankDb.Withdraw.La connessione di database o di rete potrebbe andare persa o le autorizzazioni per il database potrebbero essere errate.Un errore della chiamata m_bankDB.Withdraw provocherebbe l'esito negativo del test per motivi non correlati al codice interno.
La soluzione di Microsoft Fakes
Microsoft Fakes crea un assembly contenente le classi e i metodi che è possibile sostituire alle classi dei metodi di unit test che provocano dipendenze.Una classe sostitutiva nel modulo Fakes generato dichiara un metodo e un delegato per ogni metodo pubblico nel componente di destinazione.In un metodo di test si implementa il delegato per creare il comportamento esatto della chiamata di dipendenza nel metodo da testare.
Nell'esempio è possibile creare un assembly Fakes per il progetto BankDb e quindi usare la classe StubIBankDb generata da Fakes e derivata dall'interfaccia IBankDb per rimuovere l'incertezza causata da interazioni con il database.Una versione modificata del metodo di test Withdraw_ValidAmount_ChangesBalance avrebbe quindi un aspetto analogo al seguente:
[TestMethod]
public void Withdraw_ValidAmount_ChangesBalance()
{
// arrange
double currentBalance = 10.0;
double withdrawal = 1.0;
double expected = 9.0;
// set up the Fakes object and delegate
var stubBankDb = new MyBank.Stubs.StubIBankDb();
stubBankDb.WithdrawDoubleDouble = (id, amount) => { return 9.0; }
var account = new CheckingAccount("JohnDoe", currentBalance, stubBankDb);
// act
account.Withdraw(withdrawal);
double actual = account.Balance;
// assert
Assert.AreEqual(expected, actual);
}
Questa riga del metodo di test:
stubBankDb.WithdrawDoubleDouble = (id, amount) => { return 9.0; }
implementa il delegato Fakes per il metodo Withdraw usando un'espressione lambda.Il metodo stubBankDb.Withdraw chiama il delegato e quindi restituisce sempre la quantità specificata, permettendo al metodo di test di verificare in modo affidabile il comportamento del metodo Accounts.
Altre informazioni su Microsoft Fakes
Microsoft Fakes usa due approcci per la creazione delle classi sostitutive:
Gli stub generano classi sostitutive derivate dall'interfaccia padre della classe di dipendenza di destinazione.I metodi stub possono sostituire metodi pubblici virtuali della classe di destinazione.
Gli shim usano strumentazione di runtime per deviare chiamate a un metodo di destinazione, indirizzandole a un metodo shim sostitutivo per metodi non virtuali.
In entrambi gli approcci si usano i delegati generati delle chiamate per il metodo di dipendenza per specificare il comportamento desiderato nel metodo di test.
Per altre informazioni, vedere Isolamento del codice sottoposto a test con Microsoft Fakes.