Il presente articolo è stato tradotto automaticamente.
Visual Studio 2015
Crea software migliore grazie agli unit test intelligenti
Il mondo dello sviluppo software è sfreccia verso cicli di rilascio mai accorciare. Il tempo quando il team di sviluppo software potrebbe rigorosamente sequenziare le funzioni delle specifiche, implementazione e test in un modello a cascata è lungo passato. Sviluppo di software di alta qualità è difficile in un mondo frenetico e chiede una rivalutazione delle metodologie di sviluppo esistenti.
Per ridurre il numero di bug in un prodotto software, tutti i membri del team devono essere d'accordo su quello che dovrebbe fare il sistema software, e che è una sfida chiave. Specifiche, implementazione e collaudo in genere successo nei silos, con nessun mezzo comune di comunicazione. Lingue diverse o manufatti utilizzati per ogni reso difficile per loro di co-evolvere il progredire di attività di implementazione software, e così, mentre un documento di specifica dovrebbe collegare il lavoro di tutti i membri della squadra, che è raramente il caso in realtà. La specifica originale e l'effettiva implementazione potrebbe divergere, e l'unica cosa che tiene tutto insieme alla fine è il codice che finisce per incarnare la specifica finale e le varie decisioni di progettazione fatte in rotta. Test tenta di conciliare questa divergenza di ricorrere a ben capito-to-end a pochi scenari di test.
Questa situazione può essere migliorata. Un comune mezzo per specificare che il comportamento previsto del sistema software è necessario, uno che può essere condiviso tra progettazione, realizzazione e collaudo, e che è facile ad evolversi. La specifica deve essere direttamente correlata al codice, e il mezzo essere codificata come un'esauriente suite di test. Tecniche basate su strumento abilitati di Smart Unit test possono aiutare a soddisfare questa esigenza.
Smart Unit test
Smart Unit test, una funzionalità di anteprima di Visual Studio 2015 (vedere Figura 1), è un assistente intelligente per lo sviluppo di software, aiutando il dev team trovare bug presto e ridurre i costi di manutenzione di prova. È basata sul lavoro precedente di Microsoft Research chiamato "Pex". Suo motore utilizza analisi codice scatola bianca e vincolo risolvendo per sintetizzare i valori di input di test precisi per esercitare tutti i percorsi di codice nel codice sotto test, persistono questi come una suite compatta di tradizionale unit test con alta copertura e automaticamente evolvere la suite di test come si evolve il codice.
Figura 1 Smart Unit test è completamente integrato nell'anteprima Visual Studio 2015
Inoltre e questo è fortemente incoraggiato, correttezza proprietà specificata come asserzioni nel codice possono essere utilizzate per guidare l'ulteriore generazione di test case.
Per impostazione predefinita, se non far nulla di più di eseguire Unit test Smart su un pezzo di codice, i casi di test generati catturare il comportamento osservato del codice sotto test per ognuno il sintesi dei valori di input. In questa fase, ad eccezione di casi di test causando errori di runtime, le restanti sono ritenuti essere superato prove — dopo tutto, che è il comportamento osservato.
Inoltre, se si scrivono asserzioni specificando le proprietà di correttezza del codice sotto test, quindi Smart Unit test verrà con test i valori di input che possono causare le asserzioni a fallire, pure, ognuno scoprendo un bug nel codice, tale valore di input e quindi un caso di test fallito. Smart Unit test non può venire con tali proprietà di correttezza da sè; scrivere in base alle tue conoscenze di dominio.
Generazione di test Case
In generale, tecniche di analisi del programma rientrano tra i seguenti due estremi:
- Tecniche di analisi statica verificare che una proprietà vale su tutti i percorsi di esecuzione. Perché l'obiettivo è la verifica del programma, queste tecniche sono di solito troppo conservatore e segnalare possibili violazioni come errori, portando a falsi positivi.
- Tecniche di analisi dinamica verificare che una proprietà vale su alcuni percorsi di esecuzione. Test adotta un approccio di analisi dinamica che mira a individuare bug, ma è solitamente non può dimostrare l'assenza di errori. Quindi, queste tecniche spesso non riescono a rilevare tutti gli errori.
Potrebbe non essere possibile rilevare bug proprio quando si applica la sola analisi statica o impiegando una tecnica di test che non è a conoscenza della struttura del codice. Ad esempio, si consideri il codice seguente:
int Complicated(int x, int y)
{
if (x == Obfuscate(y))
throw new RareException();
return 0;
}
int Obfuscate(int y)
{
return (100 + y) * 567 % 2347;
}
Tecniche di analisi statica tendono ad essere conservatori, così l'aritmetica integer non lineare presente in Obfuscate cause tecniche di analisi più statici di emettere un avviso su un potenziale errore nel complicato. Inoltre, tecniche di collaudo casuale hanno poche possibilità di trovare un paio di x e y valori che innescare l'eccezione.
Unit test Smart implementa una tecnica di analisi che cade fra questi due estremi. Simili alle tecniche di analisi statica, dimostra che una proprietà vale per percorsi più fattibile. Simili alle tecniche di analisi dinamica, segnala solo gli errori veri e non falsi positivi.
Generazione di test caso coinvolge i seguenti:
- Dinamicamente alla scoperta di tutti i rami (espliciti e impliciti) nel codice sotto test.
- Sintetizzando prova precisi valori di input che esercitano quei rami.
- Registrazione dell'output il codice sotto test per gli ingressi ha detti.
- Persistenza di questi come una suite di test compatto con alta copertura.
Figura 2 Mostra come funziona utilizzando il runtime strumentazione e monitoraggio e qui vengono descritti i passaggi necessari:
- Il codice sotto test prima strumentazione e richiamate sono piantati che permetterà al motore di test di monitoraggio dell'esecuzione. Viene quindi eseguito il codice con più semplice pertinente concreto valore di input (in base al tipo del parametro). Questo rappresenta il caso test iniziale.
- Il motore di test controlla l'esecuzione, calcola la copertura per ogni test case e canzoni come valore di input scorre attraverso il codice. Se tutti i percorsi sono coperti, il processo si arresta; tutti comportamenti eccezionali sono considerati come rami, proprio come i rami espliciti nel codice. Se non sono stati coperti tutti i percorsi ancora, il motore di test seleziona un test case che raggiunge un punto del programma da cui esce un ramo scoperto, e determina come la condizione di ramificazione dipende dal valore di input.
- Il motore costruisce un sistema di vincolo che rappresenta la condizione sotto cui controllo raggiunge a quel punto del programma e sarebbe poi proseguire lungo il ramo precedentemente scoperto. Interroga un risolutore di vincoli per sintetizzare un nuovo valore di input concreto basato su questo vincolo.
- Se il solutore di vincolo può determinare un concreto valore di input per il vincolo, viene eseguito il codice sotto test con il nuovo valore di input concreto.
- Se la copertura aumenta, viene emesso un test case.
Figura 2 come caso Test generazione funziona sotto il cofano
Passaggi da 2 a 5 sono ripetute fino a quando tutti i rami sono coperti, o fino a che non vengano superati i limiti di esplorazione preconfigurati.
Questo processo è chiamato un "esplorazione". All'interno di un'esplorazione, il codice sotto test può essere "eseguito" parecchie volte. Alcuni di quei funzionamenti aumentare la copertura, e solo le esecuzioni che aumentano copertura emettono test case. Così, tutti i test generati esercitano percorsi fattibili.
Esplorazione limitata
Se il codice sotto test non contiene cicli o ricorsione illimitata, esplorazione in genere si arresta rapidamente perché ci sono solo un numero finito (piccolo) di analizzare i percorsi di esecuzione. Tuttavia, più interessanti programmi contengono loop o ricorsione illimitata. In tali casi, il numero di percorsi di esecuzione è (quasi) infinito, ed è generalmente indecidibile se una dichiarazione è raggiungibile. In altre parole, un'esplorazione vorrebbero per sempre per analizzare tutti i percorsi di esecuzione del programma. Poiché la generazione di test comporta effettivamente in esecuzione il codice sotto test, come Proteggete da tale esplorazione fuggiasco? Ecco dove limitata esplorazione gioca un ruolo chiave. Esso assicura esplorazioni interrompere dopo un ragionevole lasso di tempo. Ci sono diversi limiti di esplorazione a più livelli, configurabili utilizzate:
- Limiti di risolutore di vincoli limitano la quantità di tempo e memoria del Risolutore può utilizzare nella ricerca per il prossimo valore di input concreto.
- Limiti di percorso di esplorazione limitano la complessità del percorso di esecuzione analizzata in termini di numero di rami presi, il numero delle condizioni sopra gli ingressi che ha bisogno di essere controllato e la profondità del percorso di esecuzione in termini di stack frame.
- Limiti di esplorazione limitano il numero di "Run" che non producono un test case, il numero totale di funzionamenti consentiti e un limite di tempo totale dopo che fermate di esplorazione.
Un aspetto importante per qualsiasi approccio basato sugli strumenti di test essendo efficace è il feedback rapido, e tutti questi limiti sono stati preconfigurati per consentire un utilizzo interattivo rapido.
Inoltre, il motore di prova utilizza euristiche per raggiungere alte code coverage rapidamente rinviando risolvere sistemi di vincolo rigido. Si può lasciare che il motore di generare rapidamente alcuni test per il codice su cui stai lavorando. Tuttavia, per affrontare i problemi di generazione ingresso dura prova restanti, è possibile comporre le soglie per far il test crunch motore ulteriormente sui sistemi di vincolo complicato.
Con parametri Unit test
Tutte le tecniche di analisi del programma tenta di convalidare o confutare determinate proprietà specificate di un determinato programma. Ci sono diverse tecniche per specificare le proprietà del programma:
- API contratti specificano il comportamento delle singole azioni di API dal punto di vista dell'implementazione. Il loro obiettivo è quello di garantire robustezza, nel senso che le operazioni non crash e invarianti di dati sono conservati. Un problema comune delle API contratti è la loro vista stretta su singole azioni di API, che lo rende difficile da descrivere i protocolli a livello di sistema.
- Unit test incarnano gli scenari di utilizzo esemplare dal punto di vista di un client dell'API. Il loro obiettivo è di garantire la correttezza funzionale, nel senso che l'interazione di diverse operazioni si comporta come previsto. Un problema comune di unit test è che sono indipendente dai dettagli di implementazione dell'API.
Smart Unit test consente con parametri unit test, che unisce entrambe le tecniche. Supportato da un motore di generazione di ingresso test, questa metodologia combina il client e le prospettive di implementazione. Le proprietà di correttezza funzionale (con parametri unit test) vengono controllate sulla maggior parte dei casi dell'implementazione (generazione ingresso test).
Un test di unità con parametri (PUT) è la semplice generalizzazione di un test di unità attraverso l'uso di parametri. Una PUT rende dichiarazioni sul comportamento del codice per un intero insieme di possibili valori di input, invece di solo un singolo esemplare valore di input. Esprime ipotesi su input di test, esegue una sequenza di azioni e rivendica la proprietà che dovrebbe tenere nello stato finale; cioè, serve come la specifica. Una tale specifica non richiedono o introdurre qualsiasi nuova lingua o artefatto. È scritto a livello delle API effettive implementate il prodotto software e nel linguaggio di programmazione del prodotto software. Progettisti possono usare per esprimere il comportamento previsto del software API, gli sviluppatori possono utilizzare per guidare sviluppatore automatizzato test e tester li può sfruttare per la generazione di approfonditi test automatico. Ad esempio, la seguente mise asserisce che, dopo l'aggiunta di un elemento in un elenco diverso da null, l'elemento infatti è contenuto nell'elenco:
void TestAdd(ArrayList list, object element)
{
PexAssume.IsNotNull(list);
list.Add(element);
PexAssert.IsTrue(list.Contains(element));
}
Mette distinto le seguenti due preoccupazioni:
- La specifica le proprietà di correttezza del codice sotto test per tutti gli argomenti possibili test.
- L'effettivo "chiuso" test case con argomenti concreti.
Il motore emette gli stub per la prima preoccupazione, e siete incoraggiati a carne fuori basato sulla tua conoscenza di dominio. Chiamate successive di Smart Unit test automaticamente generare e aggiornare i singoli casi di test chiusi.
Applicazione
Il team di sviluppo software può essere radicato in varie metodologie già, e non è realistico aspettarsi loro di abbracciare uno nuovo durante la notte. Infatti, Smart Unit test non è intesa come una sostituzione per qualsiasi test squadre pratica potrebbe essere seguito; piuttosto, è destinata ad aumentare eventuali pratiche esistenti. Adozione rischia di iniziare con un abbraccio graduale, con squadre sfruttando le funzionalità di test automatico predefinito generazione e manutenzione prima e poi passare a scrivere le specifiche nel codice.
Test di comportamento osservato immaginare di dover apportare modifiche a un corpo di codice con nessuna copertura di test. È possibile definire con precisione il comportamento in termini di una suite di test di unità prima di iniziare, ma che è più facile a dirsi che a farsi:
- Il codice (codice prodotto) potrebbe non prestarsi ad essere unità testabili. Potrebbe avere dipendenze strette con l'ambiente esterno che dovrà essere isolato, e se non li posto, potrebbe non sai nemmeno dove cominciare.
- La qualità delle prove potrebbe anche essere un problema, e ci sono molte misure di qualità. C'è la misura di copertura — quanti rami, o percorsi di codice, o altri elementi di programma, nel codice prodotto fanno il tocco di test? C'è la misura di affermazioni che esprimono se il codice sta facendo la cosa giusta. Nessuna di queste misure di per sé è sufficiente, tuttavia. Invece, ciò che sarebbe bello è un'alta densità di asserzioni da convalidare con alte code coverage. Ma non è facile fare questo tipo di analisi di qualità nella tua testa come si scrivono i test e, di conseguenza, si potrebbe finire con le prove che esercitano gli stessi percorsi di codice ripetutamente; forse solo testare il percorso"felice" e non si saprà mai se il codice prodotto può far fronte anche con tutti quei casi di bordo.
- E frustrante, non si potrebbero sapere anche quali asserzioni per mettere. Immagina di essere chiamati ad apportare modifiche a un codice non conosce base!
La capacità di generazione di test automatico della Smart Unit test è particolarmente utile in questa situazione. È possibile la linea di base corrente osservata il comportamento del tuo codice come una suite di test per l'uso come una suite di regressione.
Specifiche-Based Testing Team Software possono utilizzare mette come la specifica di generazione esaustiva caso test drive per scoprire violazioni di asserzioni di prova. Liberata di gran parte del lavoro manuale necessario scrivere test case che raggiungere alte code coverage, le squadre possono concentrarsi sui compiti che Smart Unit test non è possibile automatizzare, come scritto più interessanti scenari come mette e sviluppo integrazione prove che vanno oltre l'ambito di mette.
Ricerca automatica di Bug affermazioni che esprimono proprietà di correttezza possono essere dichiaratoe in diversi modi: come affermare dichiarazioni, come codice contratti e altro ancora. La cosa bella è che questi sono tutti compilati fino a rami — un se istruzione con un ramo allora e un altro ramo che rappresenta il risultato del predicato essere rivendicato. Perché Smart Unit test calcola gli ingressi che esercita tutti i rami, diventa uno strumento efficace ricerca dei bug, pure — qualsiasi ingresso si esce con che può grilletto il ramo else rappresenta un bug nel codice sotto test. Così, tutti i bug segnalati sono bug effettivo.
Ridotta manutenzione Test Case in presenza di mette, un numero significativamente inferiore di casi di test desidera essere mantenute. In un mondo dove chiusi test singoli casi sono stati scritti manualmente, cosa sarebbe successo quando si è evoluto il codice sotto test? Si dovrà adattare individualmente, il codice di tutti i test che potrebbe rappresentare un costo notevole. Ma scrivendo mette invece, devono essere mantenute solo le mette. Poi, Smart Unit test può rigenerare automaticamente i singoli casi di test.
Sfide
Strumento limitazioni la tecnica di usando analisi codice scatola bianca con vincolo risolvendo funziona molto bene su codice a livello di unità che ha ben isolato. Tuttavia, il motore di prova hanno alcune limitazioni:
- Lingua: In linea di principio, il motore di prova può analizzare programmi .NET arbitrari, scritti in qualsiasi linguaggio .NET. Tuttavia, il codice di test viene generato solo in c#.
- Non-determinismo: Il motore di prova presuppone che il codice sotto test è deterministico. Se così non fosse, esso sarà potare i percorsi di esecuzione non deterministico, o potrebbe andare in cicli fino a quando non colpisce limiti di esplorazione.
- Della concorrenza: Il motore di prova non consente di gestire programmi multithread.
- Codice nativo o codice .NET che non strumentazione: Il motore di prova non capisce codice nativo, ovvero x86 istruzioni chiamato attraverso la funzione di richiamo piattaforma (P/Invoke) di Microsoft .NET Framework. Il motore di prova non sa come tradurre tali chiamate in vincoli che possono essere risolti da un risolutore di vincoli. E anche per il codice .NET, il motore può solo analizzare il codice che gli strumenti.
- Galleggiante aritmetica: Il motore di test utilizza un solutore automatico vincolo per determinare i valori che sono rilevanti per il caso di test e il codice sotto test. Tuttavia, l'abilità di risolutore di vincolo sono limitate. In particolare, esso non può ragionare proprio sull'aritmetica a virgola.
In questi casi i test allarmi motore lo sviluppatore emettendo un avviso e il comportamento del motore in presenza di tali limiti può essere controllato utilizzando gli attributi personalizzati.
Scrittura buona parametrizzata Unit test scrittura mette buone può essere difficile. Ci sono due domande fondamentali per rispondere:
- Copertura: Quali sono buoni scenari (sequenze di chiamate di metodo) di esercitare il codice sotto test?
- Verifica: Quali sono le asserzioni di buone che possono essere dichiaratoe facilmente senza reimplementato l'algoritmo?
Una PUT è utile solo se esso fornisce risposte per entrambe le domande.
- Senza copertura sufficiente; Se lo scenario è troppo stretto per raggiungere tutto il codice sotto test, la misura del PUT è limitata.
- Senza sufficienti verifiche di risultati calcolati; cioè, se il metti non contengono abbastanza asserzioni, non può controllare che il codice sta facendo la cosa giusta. Tutto il PUT non quindi è controllare che il codice sotto test non crash o errori di runtime.
Nel tradizionale unit test, la serie di domande comprende un altro: Quali sono gli ingressi di prova pertinenti? Con mette, questa domanda è curata dall'utensileria. Tuttavia, il problema di trovare buone asserzioni è più facile nel tradizionale unit test: Le asserzioni tendono ad essere più semplice, perché stai scritte per ingressi di prova particolare.
Il confezionamento
La funzionalità Smart Unit test in Visual Studio 2015 anteprima consente di specificare il comportamento previsto del software in termini di relativo codice sorgente e utilizza analisi automatizzata codice scatola bianca in congiunzione con un risolutore di vincoli per generare e mantenere una compatta suite delle prove pertinenti con alta copertura per codice .NET. I benefici abbracciano funzioni — progettisti li possono utilizzare per specificare il comportamento previsto di software API; gli sviluppatori possono utilizzare per guidare sviluppatore automatizzato test; e tester li può sfruttare per la generazione di approfonditi test automatico.
I cicli di rilascio mai accorciare nello sviluppo di software è guida gran parte delle attività legate alla pianificazione, specifiche, implementazione e verifica che accadono continuamente. Questo mondo frenetico è impegnativo ci a rivalutare le pratiche esistenti intorno a tali attività. Breve, veloce, cicli iterativi richiedono prendendo la collaborazione tra queste funzioni ad un nuovo livello. Caratteristiche come Smart Unit test possono aiutare il team di sviluppo software più facilmente raggiungono tali livelli.
Pratap Lakshman lavora nella divisione Developer presso Microsoft, dove attualmente è un senior program manager del team di Visual Studio , lavorando su strumenti di test.
Grazie al seguente Microsoft esperto tecnico per la revisione di questo articolo: Nikolai Tillmann