Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Porfirio Tramontana » 22.Testing di Integrazione e di Sistema


Livelli di testing

livello produttore
unit testing (Testing di Unità)
integration testing (Testing di Integrazione)
system testing (Testing di Sistema)

livello cooperativo produttore-cliente privilegiato
alpha testing
beta testing

livello cliente o utente
acceptance testing (Testing di accettazione)

Testing di Sistema

Richiede che i vari componenti vengano integrati ed il successivo test del sistema (o sotto-sistema) integrato.

Per sistemi complessi, richiede due fasi:

  • Integration testing – il team di test accede al codice sorgente. Il sistema viene testato man mano che I vari componenti vengono aggiunti. Punta a trovare I difetti.
  • Release testing – il team di test testa la versione da rilasciare come una scatola nera (black-box). É più una convalida fatta per dimostrare che il sistema funziona. Se è coinvolto anche il cliente, si parla di Test di Accettazione.

Testing di Integrazione

Richiede la costruzione del sistema a partire dai suoi componenti ed il testing del sistema risultante per scoprire problemi che nascono dall’interazione fra I vari componenti.

Richiede l’identificazione di gruppi di componenti che realizzano le varie funzionalità del sistema e la loro integrazione mediante componenti che li fanno lavorare insieme.

Partendo da una architettura organizzata gerarchicamente, le integrazioni possono essere realizzate con approccio top-down o bottom-up o misto.

Esecuzione del test

  • Costruzione di Moduli guida (driver)
    • invocano l’unità sotto test, inviandole opportuni valori, relativi al test case.
  • Moduli fittizi (stub)
    • sono invocati dall’unità sotto test;
    • emulano il funzionamento della funzione chiamata rispetto al caso di test richiesto (tenendo conto delle specifiche della funzione chiamata)
      • Quando la funzione chiamata viene realizzata e testata, si sostituisce lo stub con la funzione stessa.

Driver

Tramite un framework di Testing Automation come JUnit realizzare driver e stub necessari per testare un modulo non terminale.

Le classi di test per JUnit rappresentano dei driver quando si riferiscono ad una classe non accessibile dall’esterno;
Un driver deve avere la visibilità dei componenti da testare (o quanto meno dell’interfaccia che vogliamo esercitare).

Stub

Uno stub è una funzione fittizia la cui correttezza è vera per ipotesi.
Esempio, se stiamo testando una funzione prod_scal(v1,v2) che richiama una funzione prodotto(a,b) ma non abbiamo ancora realizzato tale funzione; nel metodo driver scriviamo il codice per eseguire alcuni casi di test:
Ad esempio chiamiamo prod_scal([2,4],[4,7])
Il metodo stub potrà essere scritto così:
int prodotto (int a, int b){
if (a==2 && b==4) return 8;
if (a==4 && b==7) return 28;
}

La correttezza di questo metodo stub è data per ipotesi.
Ovviamente per poter impostare tale testing, bisognerà avere precise informazioni sul comportamento interno richiesto al modulo da testare.

Strategie per il testing di integrazione


Regression Testing

Man mano che si integrano i vari componenti, sarà anche necessario rieseguire i precedenti test.

Se si scoprono nuovi difetti, si dovrà capire se erano già presenti nei precedenti incrementi, o se sono causati da quelli nuovi.

Il test di regressione si esegue anche dopo un intervento di manutenzione (per verificare che il sistema non sia ‘regredito’).

Confronti fra i vari approcci

  • Per la validazione dell’architettura
    • Il Testing di integrazione Top-down scopre meglio gli errori presenti nell’architettura.
  • Per la dimostrazione del funzionamento del sistema
    • Il Testing di integrazione Top-down permette una dimostrazione (limitata) fin dalle prime fasi dello sviluppo.
  • Implementazione del Test
    • In genere è più semplice con l’approccio bottom-up.
  • Osservazione del test
    • Entrambi gli approcci hanno problemi. Può essere necessario codice extra in entrambi i casi.

Testing di Release

É il processo di testing di una release del sistema che sarà rilasciata al cliente.
Obiettivo primario è dimostrare che il sistema si comporti come specificato.
Occorre provare che fornisce tutte le funzionalità, le prestazioni, l’affidabilità, etc. richieste, e che non fallisca.

É in genere un testing black-box (a scatola nera) detto anche testing funzionale basato solo sulle specifiche del software;
I Tester non accedono al suo codice.

Performance testing

Dopo che il sistema è stato completamente integrato, è possibile testarne le proprietà emergenti, come le prestazioni ed affidabilità.

I test di prestazione in genere riguardano il carico e si pianificano test in cui il carico viene incrementato progressivamente finché le prestazioni diventano inaccettabili.

Il carico deve essere progettato in modo da rispecchiare le normali condizioni di utilizzo.

Stress testing

Nello stress testing si sollecita il sistema con un carico superiore a quello massimo previsto: in queste condizioni in genere i difetti vengono alla luce.

Stressando il sistema si può testare il comportamento in caso di fallimento.

L’eventuale fallimento dovrebbe essere ‘leggero’ e non produrre effetti catastrofici. Si deve controllare che non ci siano perdite inaccettabili di servizio e di dati.

Lo Stress testing è particolarmente rilevante per sistemi distribuiti che possono mostrare severe degradazioni delle prestazioni quando la rete è sommersa di richieste.

Piano di test

Documento relativo all’intero progetto.
Struttura

  • specifica delle unità di test (per un dato livello di test) Es. Modulo, gruppi di moduli, programma, sottosistemi, intero sistema;
  • Caratteristiche da testare: funzionalità, prestazioni, vincoli di progetto, sicurezza…
  • Approccio: criterio di selezione dei test, criterio di terminazione, strumenti;
  • Prodotti del test: es. Casi di test, rapporto finale, diario del test, statistiche di copertura…
  • Schedulazione: quando effettuare il testing e lo sforzo per attività;
  • Allocazione del personale.

Rapporti sul test

Diario del test

  • descrive i dettagli del test per come si è svolto effettivamente;
  • la specifica dei casi di test può essere completata e usata come diario.

Riepilogo del test

  • Rivolto al management del progetto
    • numero totale di casi di test eseguiti;
    • numero e tipo di malfunzionamenti osservati;
    • numero e tipo di difetti scoperti.

Sommario dei malfunzionamenti

  • Rivolto a chi deve effettuare il debugging o la correzione.

Testing di Classi di oggetti

La completa copertura di una classe richiede di:

  • Testare tutte le operazioni di un oggetto;
  • Settare ed interrogare tutti gli attributi di un oggetto;
  • Esercitare l’oggetto in tutti I possibili stati.

L’ereditarietà rende più difficile la scelta dei casi di test degli oggetti giacchè le informazioni da testare non sono localizzate (ma distribuite nella gerarchia di ereditarietà).

Testing vs Ispezione

Testing e ispezione sono due approcci complementari al problema della verifica del software.

L’Ispezione mira alla verifica della correttezza di un programma, tramite analisi statica e lettura del codice:

  • Si basa sull’analisi statica dei programmi;
  • Esistono tecniche basati su modelli formali per la valutazione della correttezza degli algoritmi.

Il Testing mira alla ricerca degli errori in un programma tramite l’osservazione del suo comportamento e il confronto con il comportamento atteso

  • Si basa sull’analisi dinamica dei processi.

Testing vs Ispezione (segue)

L’ispezione è possibile anche per algoritmi che non siano stati ancora implementati, oppure per programmi incompleti.

Il testing è possibile solo per codice che possa essere eseguito.

Sia l’ispezione che il testing possono essere svolte manualmente oppure automatizzate.
Sia l’ispezione che il testing mirano a stabilire l’esistenza di bugs, ma è durante il processo di debugging che si vanno a localizzare e correggere i difetti.

Debugging

Attività di ricerca e correzione dei difetti che sono causa di malfunzionamenti.

Il debugging è ben lungi dall’essere stato formalizzato;
Metodologie e tecniche di debugging rappresentano soprattutto un elemento dell’esperienza del programmatore/tester.

Localizzazione dei difetti

Ridurre la distanza tra difetto e malfunzionamento.

Mantenendo un’immagine dello stato del processo in esecuzione in corrispondenza dell’esecuzione di specifiche istruzioni.

  • Watch point e variabili di watch
    • Un watch, in generale, è una semplice istruzione che inoltra il valore di una variabile verso un canale di output:
      • L’inserimento di un watch (sonda) è un’operazione invasiva nel codice: anche nel watch potrebbe annidarsi un difetto;
      • In particolare l’inserimento di sonde potrebbe modificare sensibilmente il comportamento di un software concorrente.
  • Asserzioni, espressioni booleane dipendenti da uno o più valori di variabili legate allo stato dell’esecuzione.

Metodologie per il debugging

“Forza bruta” (brute force)
Si dissemina il codice di sonde per catturare quante più informazioni possibili e valutarli, in cerca di indizi localizzati del malfunzionamento.

Backtracking
Si cerca di ripercorrere il codice “all’indietro” a partire dal punto dove si è verificato il malfunzionamento (istruzione di output oppure eccezione);
Analogamente alla tecnica delle Path Condition diventa via via più difficile procedere all’indietro all’allargarsi del campo di possibilità.

Eliminazione delle cause
Prima di tutto, si individua la tipologia dei dati che fanno fallire il programma.
Poi si cerca di formulare un’ipotesi sulla possibile causa del difetto, proponendo dati in ingresso in grado di far avvenire il malfunzionamento, poi si cerca di controllare la validità di tale ipotesi.

Automatizzazione del debugging

Il debugging è un’attività estremamente intuitiva, che però deve essere operata nell’ambito dell’ambiente di sviluppo e di esecuzione del codice.

Strumenti a supporto del debugging sono quindi convenientemente integrati nelle piattaforme di sviluppo, in modo da poter accedere ai dati del programma, anche durante la sua esecuzione, senza essere invasivi rispetto al codice.

In assenza di ambienti di sviluppo, l’inserimento di codice di debugging invasivo rimane l’unica alternativa.

Funzionalità comuni di debugging

  • Inserimento break point.
  • Esecuzione passo passo del codice
    • Entrando o meno all’interno dei metodi chiamati;
    • Uscendo.
  • Verifica di asserzioni
    • Le asserzioni possono anche essere utilizzate come parametri per break point condizionali.
  • Valutazione (watch) del valore delle variabili, mentre l’esecuzione è in stato di interruzione
    • Nei linguaggi interpretati (tra cui anche Java), è possibile anche fornire la possibilità di modificare in fase di esecuzione il valore di variabili, semplificando notevolmente il problema della ricerca di casi di test in grado di replicare il malfunzionamento.

I materiali di supporto della lezione

Sommerville, Ingegneria del Software, 8° edizione, Capitoli 22-23-24.

  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion