Generazione di input con l'esecuzione simbolica dinamica

IntelliTest genera input per gli unit test con parametri analizzando le condizioni di ramo nel programma. Gli input di test vengono scelti in base alla loro capacità di attivare nuovi comportamenti di creazione rami del programma. L'analisi è un processo incrementale. Ottimizza un predicato q: I -> {true, false} in base ai parametri di input del test formale I. q rappresenta il set di comportamenti già osservati da IntelliTest. Inizialmente q := false perché non è ancora stato osservato nessun comportamento.

Le fasi del ciclo sono:

  1. IntelliTest determina input i come q(i)=false mediante un risolutore di vincoli. Per costruzione, l'input i adotta un percorso di esecuzione non rilevato in precedenza. Inizialmente questo significa che i può essere qualsiasi input, perché non è ancora stato scoperto alcun percorso di esecuzione.

  2. IntelliTest esegue il test con l'input i scelto e controlla l'esecuzione del test e il programma sottoposto al test.

  3. Durante l'esecuzione il programma accetta un percorso specifico, determinato da tutti i rami condizionali del programma. Il set di tutte le condizioni che determinano l'esecuzione è detto condizione di percorso, scritta sotto forma di predicato p: I -> {true, false} sui parametri di input formali. IntelliTest calcola una rappresentazione del predicato.

  4. IntelliTest imposta q := (q or p). In altre parole, registra il fatto che ha rilevato il percorso rappresentato da p.

  5. Andare al passaggio 1.

Il risolutore di vincoli di IntelliTest può gestire valori di tutti i tipi presenti nelle applicazioni .NET:

IntelliTest esclude gli input che violano i presupposti dichiarati.

Oltre agli input immediati (argomenti di unit test con parametri), un test può estrarre altri valori di input dalla classe statica PexChoose. Le scelte determinano anche il comportamento degli oggetti fittizi con parametri.

Risolutore di vincoli

IntelliTest usa un risolutore di vincoli per determinare i valori di input rilevanti per il test e il programma sottoposto a test.

IntelliTest usa il risolutore di vincoli Z3.

Code coverage dinamico

Come effetto collaterale del monitoraggio in fase di esecuzione, IntelliTest consente di raccogliere dati di code coverage dinamico. Il termine dinamico indica che IntelliTest agisce solo su codice già eseguito, pertanto non può specificare valori assoluti per il coverage come fanno altri strumenti.

Ad esempio, quando IntelliTest segnala come code coverage dinamico 5/10 blocchi di base, ciò significa che sono stati analizzati cinque blocchi su dieci e che dieci è il numero totale di blocchi in tutti i metodi raggiunti finora dall'analisi (e non di tutti i metodi esistenti nell'assembly sottoposto a test). In una fase successiva dell'analisi, man mano che vengono rilevati altri metodi raggiungibili, è possibile che aumentino sia il numeratore (5) che il denominatore (10) della frazione di questo esempio.

Integer e float

Il risolutore di vincoli di IntelliTest determina i valori di input di test dei tipi primitivi quali byte, int, float e altri ancora, per attivare percorsi di esecuzione diversi per il test e il programma sottoposto a test.

Oggetti

IntelliTest può creare istanze di classi .NET esistenti oppure può essere usato per creare automaticamente oggetti fittizi che implementano un'interfaccia specifica e presentano comportamenti diversi a seconda dell'uso.

Creare istanze di classi esistenti

Definizione del problema

IntelliTest consente di monitorare le istruzioni completate quando esegue un test e il programma sottoposto a test. In particolare esegue il monitoraggio di tutti gli accessi ai campi. Quindi usa un risolutore di vincoli per determinare i nuovi input di test, inclusi gli oggetti e i valori dei campi corrispondenti, in modo che il test e il programma sottoposto a test adottino altri comportamenti interessanti.

Ciò significa che IntelliTest deve creare oggetti di determinati tipi e impostare i relativi valori di campo. Se la classe è visible e ha un costruttore predefinito visible, IntelliTest può creare un'istanza della classe. Se tutti i campi della classe sono visible, IntelliTest può impostare i campi automaticamente.

Se il tipo non è visibile o i campi non sono visible, IntelliTest richiede un apporto esterno per creare oggetti e impostare stati interessanti al fine di ottenere il massimo code coverage. Sebbene IntelliTest possa usare la reflection per creare e inizializzare istanze in modo arbitrario, questo approccio non è solitamente consigliabile poiché può impostare l'oggetto in uno stato che non può mai verificarsi durante la normale esecuzione del programma. IntelliTest si basa invece sui suggerimenti specificati dall'utente.

Visibilità

.NET offre un modello di visibilità elaborato: i tipi, i metodi, i campi e gli altri membri possono essere private, public, internal e altro ancora.

Quando IntelliTest genera test, prova a eseguire solo azioni (ad esempio la chiamata di costruttori o metodi e l'impostazione dei campi) valide in base alle regole di visibilità .NET all'interno del contesto dei test generati.

Di seguito vengono definite le regole:

  • Visibilità dei membri interni

    • IntelliTest presuppone che i test generati avranno accesso ai membri interni che risultano visibili alla classe PexClass di inclusione. Per estendere ad altri assembly la visibilità dei membri interni, .NET dispone di InternalsVisibleToAttribute.
  • Visibilità di membri privati e della famiglia (protected in C#) della classe PexClass

    • IntelliTest inserisce sempre direttamente i test generati nella classe PexClass o in una sottoclasse. Pertanto IntelliTest presuppone che può usare tutti i membri della famiglia visibili (protected in C#).
    • Se i test generati vengono inseriti direttamente in PexClass (in genere usando le classi parziali), IntelliTest presuppone che può usare anche tutti i membri privati della classe PexClass.
  • Visibilità dei membri pubblici

    • IntelliTest presuppone che può usare tutti i membri esportati visibili nel contesto di PexClass.

Oggetti fittizi con parametri

Come testare un metodo che ha un parametro di un tipo di interfaccia? O di una classe non sealed? IntelliTest non può determinare quali implementazioni verranno usate dopo la chiamata di questo metodo. È anche possibile che al momento del test non sia disponibile un'implementazione reale.

La risposta convenzionale è l'uso di oggetti fittizi con comportamento esplicito.

Un oggetto fittizio implementa un'interfaccia (o estende una classe non sealed). Non rappresenta un'implementazione reale, ma solo un collegamento che consente l'esecuzione di test usando l'oggetto fittizio. Il suo comportamento è definito manualmente in ogni test case nel quale viene usato. Esistono numerosi strumenti che semplificano la definizione degli oggetti fittizi e del comportamento previsto, ma questo comportamento va comunque definito manualmente.

Anziché usare valori hard-coded negli oggetti fittizi, IntelliTest può generare i valori. Così come supporta il testing unità con parametri, IntelliTest supporta anche gli oggetti fittizi con parametri.

Per gli oggetti fittizi con parametri sono disponibili due modalità di esecuzione diverse:

  • Scelta: durante l'esplorazione del codice, gli oggetti fittizi con parametri producono input di test aggiuntivi e IntelliTest prova a scegliere valori interessanti.
  • Riesecuzione: quando si esegue un test generato in precedenza, gli oggetti fittizi con parametri funzionano come stub con comportamento (in altre parole, comportamento predefinito).

Per ottenere valori per gli oggetti fittizi con parametri, usare PexChoose.

Struct

Il procedimento di IntelliTest con i valori struct è simile a quello usato con gli oggetti.

Matrici e stringhe

IntelliTest consente di monitorare le istruzioni completate mentre esegue un test e il programma sottoposto a test. In particolare rileva quando il programma dipende dalla lunghezza di una stringa o di una matrice (e dai limiti inferiori e dalle lunghezze di una matrice multidimensionale). Rileva anche l'uso dei diversi elementi di una stringa o una matrice. Quindi usa un risolutore di vincoli per determinare le lunghezze e i valori degli elementi con i quali il test e il programma possono originare comportamenti interessanti.

IntelliTest prova a ridurre al minimo le dimensioni delle matrici e delle stringhe necessarie per attivare i comportamenti di programma interessanti.

Ottenere input aggiuntivi

La classe statica PexChoose può essere usata per ottenere input aggiuntivi per un test e per implementare oggetti fittizi con parametri.

Vuoi lasciarci dei commenti?

Pubblicare idee e richieste di funzionalità nella community degli sviluppatori.