Testing e Debugging

1 / 55
About This Presentation
Title:

Testing e Debugging

Description:

Testing e Debugging Perch ? Che cosa? Quando? GOAL: software con zero difetti MA impossibile da ottenere e garantire Necessaria una attenta e continua VERIFICA ... – PowerPoint PPT presentation

Number of Views:2
Avg rating:3.0/5.0
Slides: 56
Provided by: spazioinw9

less

Transcript and Presenter's Notes

Title: Testing e Debugging


1
Testing e Debugging
2
Perché? Che cosa? Quando?
  • GOAL software con zero difetti
  • MA impossibile da ottenere e garantire
  • Necessaria una attenta e continua VERIFICA
  • Tutto deve essere verificato documenti di
    specifica, di progetto, dati di collaudo,
    .programmi
  • Si fa lungo tutto il processo di sviluppo, NON
    solo alla fine!

3
Terminologia
  • Verifica (verification) insieme delle attività
    volte a stabilire se il programma costruito
    soddisfa le specifiche (non solo funzionali)
  • did we build the program right?
  • si assume che le specifiche esprimano in modo
    esauriente i desiderata del committente
  • Testing particolare tipo di verifica
    sperimentale fatta mediante esecuzione del
    programma, selezionando alcuni dati di ingresso e
    valutando risultati

4
Terminologia
  • Debugging localizzare errori (difetti) nel
    codice (il testing ne rivela la presenza ma non
    li localizza)
  • Programmazione difensiva insieme di tecniche di
    programmazione che cercano di evitare di
    introdurre errori, aumentano probabilità di
    correttezza e facilitano verifica e debugging
  • Convalida stabilire se il programma soddisfa le
    aspettative del committente (indipendentemente
    dalle sue specifiche)
  • Attenzione terminologia non standardizzata!

5
Testing
  • Si fanno esperimenti con il comportamento del
    programma allo scopo di scoprire eventuali errori
  • si campionano comportamenti
  • come ogni risultato sperimentale, fornisce
    indicazioni parziali relative al particolare
    esperimenti
  • programma provato solo per quei dati
  • GOAL trovare "controesempi"
  • Tecnica dinamica rispetto alle verifiche statiche
    fatte dal compilatore

6
Testing
  • Testing esaustivo (esecuzione per tutti i
    possibili ingressi) dimostra la correttezza
  • p.es. se programma calcola un valore in base a un
    valore di ingresso nel range 1..10, testing
    esaustivo consiste nel provare tutti i valori
    per le 10 esecuzioni diverse si verifica se il
    risultato è quello atteso
  • Impossibile da realizzare in generale
  • p.es. se programma legge 3 ingressi interi nel
    range 1..1000 e calcola un valore, testing
    esaustivo richiede 109 esecuzioni!
  • per programmi banali si arriva a tempi di
    esecuzione pari al tempo passato dal big-bang

7
Testing
  • Program testing can be used to show the presence
    of bugs, but never to show their absence.
    (Dijkstra 1972)
  • Obiettivo trovare dati di test che massimizzino
    la probabilità di trovare errori

8
Criteri di test
  • È cruciale la scelta di opportuni valori (dati o
    casi di test) "sufficienti a convincerci" che il
    programma è corretto
  • p.es. eseguire il programma con i valori 1, 10
    e 5.
  • In base a che cosa si determinano i casi di test?
  • in base alla specifica (test black-box o
    funzionale esiste anche un test white/glass-box,
    di cui qui non parliamo)
  • e quando?
  • idealmente, nel momento in cui si scrive la
    specifica del modulo
  • In base a quali criteri?

9
Testing funzionale
  • Proprietà
  • esprime il punto di vista di chi vuole/deve usare
    il modulo
  • esito del test comprensibile da chi non conosce
    limplementazione
  • Varie tecniche per generare dati di test a
    partire da specifiche

10
Test funzionale combinazioni proposizionali (1)
ESEMPIO
  • Combinare i vari casi alternativi espressi da una
    specifica
  • static boolean isPrime (int x)
  • //_at_ensures ( \result ltgt x è primo)
  • Scegliere dati di test primi e non primi. Es. 5 e
    8

11
Test funzionale combinazioni proposizionali (2)
  • In generale, possiamo identificare le parti in
    alternativa di una specifica espressa come
    formula di logica proposizionale usando
    loperatore
  • static float sqrt (float x, float epsilon)
  • //_at_requires x gt 0 .00001 lt epsilon lt .001
  • //_at_ensures x-epsilon lt \result \result lt
    xepsilon
  • REQUIRES congiunzione di x gt 0 con
    .00001ltepsilonlt.001
  • Parte x gt 0 equivalente a x0 xgt0
  • Combinazioni ottenibili
  • x 0 .00001 lt epsilon lt .001
  • x gt 0 .00001 lt epsilon lt .001

12
Il metodo seguito
  • Si esamina la clausola requires e, se possibile,
    la clausola effects
  • Si partiziona il dominio di ingresso in
    sottoinsiemi come specificato da essa, riducendo
    la formula proposizionale in forma
  • x1x2. y1y2 ym
  • NB metodo applicabile a clausole requires ma non
    sempre a clausola ensures
  • in questo caso non conoscendo codice impossibile
    prevedere se \result\result ltx o x o gtx come
    scegliere x?

13
Es. applicato a effects
  • Es. static int maxOfThree (int x, int y, int z)
  • //_at_ensures ( restituisce il valore massimo fra
    x, y, z )
  • Ci sono tre alternative il massimo è x, è y, o è
    z
  • Es. static int maxOfThree (int x, int y, int z)
  • //_at_ ensures \result x xgty xgtz
  • \result y ygtx ygtz
  • \result z zgtx zgty
  • Ci sono tre alternative il massimo è x, è y, o è
    z
  • Casi di test ricavabili da ensures
  • Un caso in cui il massimo è x, p. es. (5,3,0)
  • Un caso in cui il massimo è y, p. es. (7,11,2)
  • Un caso in cui il massimo è z, p. es. (7,10,12)

14
Testing funzionale (cont.)
  • Altre volte possibile e necessario usare clausola
    ensures
  • static boolean isPrime (int x)
  • //_at_ensures \result true iff x prime
  • Scegliere dati di test primi e non primi

15
Casi eccezionali
  • Testare non solo il comportamento normale ma
    anche le eccezioni
  • //_at_ensures a!null
  • //_at_ (\exists int i 0lti ilta.length xai)
    a\resultx
  • //_at_ signals (NotFoundException e)
  • //_at_ (\forall int i 0lti ilta.length x
    ! ai)
  • //_at_ signals (NullPointerException e) a null
  • static int search (int a, int x)
  • throws NotFoundException, NullPointerException
  • Testare search con array a null, con x non in a,
    con x in a
  • ?? testare anche con input che non soddisfa
    clausola requires??
  • NO, se metto requires è responsabile il cliente
  • se invece metto eccezione, devo anche testare la
    via cha la genera

è bene evitare la requires e definire funzioni
totali
16
Testing con valori limite (boundary values)
  • Se valore dellinput può stare in un intervallo,
    testare estremi dellintervallo e combinare
    valori limite
  • Esempi
  • valori estremi per i numeri (max. int
    ammissibile)
  • sqrt con radicando 0
  • stringa vuota o di 1 carattere
  • array array vuoto o di un elemento
  • elaborazioni con array considerare valori
    estremi degli indici

17
Altri esempi
  • Triangoli identificati da vertici
  • tre punti allineati
  • due punti coincidenti
  • tre punti coincidenti
  • triangolo rettangolo
  • un vertice nellorigine o sugli assi
  • .

18
Esempio
  • //_at_ensures (\result è il massimo fra x, y, z )
  • static int maxOfThree (int x, int y, int z)
  • Casi limite
  • x y z p.es. 3, 3,3
  • xy !z
  • ecc.

19
Esempio casi limite con alias
  • Due parametri si riferiscono a due oggetti
    mutabili, dello stesso tipo
  • Considerare casi in cui coincidono, anche se non
    previsto esplicitamente dalle specifiche
  • //_at_ensures( removes all elements of v2 and
    appends
  • //_at_them in reverse order to the end of v1 )
  • static void appendVector(Vector v1, Vector v2)
  • while (v2.size() gt 0) v1.addElement(v2.lastEle
    ment())
  • v2.removeElementAt(v2.size()-1)

NON è vietato che v1 e v2 siano lo stesso Vector
testando questo caso si trova un errore
20
Testing di astrazioni sui dati
  • Si effettua test per tutte le operazioni del tipo
    di dato, MA sfruttando sinergie tra metodi
    costruttori e modificatori e metodi osservatori
  • Caso di studio lastrazione IntSet

21
Specifica di IntSet
  • public class IntSet
  • /OVERVIEW insiemi di interi illimitati e
    modificabili per es. 1, 2, 10, -55 /
  • //costruttori
  • //_at_ensures (\forall int y!this.isIn(y))
  • public IntSet()
  • //metodi mutators
  • //_at_ ensures this.isIn(x) (\forall int y
    x!y
  • //_at_ \old(this.isIn(y)) ltgt this.isIn(y))
  • public void insert(int x) //_at_ ensures
    !this.isIn(x) (\forall int y x!y
  • //_at_ \old(this.isIn(y)) ltgt this.isIn(y))
  • public void remove(int x)
  • //_at_ensures (\result è true sse x è fra gli
    elementi di this)
  • public boolean isIn (int x)
  • //_at_ ensures (\result è cardinalità di this )
  • public int size()

22
Esempio Test di IntSet
  • Testing funzionale
  • valori limite generare IntSet con 0, 1 o 2 el.
  • per ognuno testare isIn (risultato false e true),
    size, elements
  • testare size dopo insert e remove, con aggiunta o
    cancellazione di elemento presente o assente
  • testare elements per insiemi di 0, 1, 2, elementi

23
Verificare RI e specifiche
  • Quando si testano classi, casi di test diventano
    molto numerosi.
  • Difficile capire se i risultati sono corretti, e
    nel caso in cui siano scorretti quale la causa
    dellerrore.
  • Per automatizzare, allora verificare
    sistematicamente RI (es. chiamando un metodo
    repOK() dopo ogni operazione di costruzione e
    modifica)
  • Es. Aggiungere repOk() (verifica invariante)
  • quando si esce da IntSet(), remove(), insert()
  • Se possibile anche verifica delle pre e
    postcondizioni
  • Tutto questo aiuta ad automatizzare il testing e
    successivamente semplifica il debugging

24
Esempio semplificare post
  • Verifica della postcondizione di insert è
    difficile perche richiede di verificare che
    tutti gli elementi già presenti nellinsieme non
    sono eliminati
  • Piu facile verificare solo che la cardinalità
    dellinsieme sia corretta
  • isIn(x)
  • (\old(isIn(x))gt size(x)\old(size(x))
  • \old(isIn(x))gtsize(x)1\old(size(x)))

25
Test delle gerarchie di tipi
  • Dati di test funzionale per sottotipo devono
    includere quelli del supertipo in generale
    sottotipo testato con
  • dati di test funzionali per supertipo, con in più
    chiamate del costruttore del sottotipo
  • dati di test funzionali aggiuntivi caratteristici
    del sottotipo

26
Test di unità e di integrazione
  • Test di unità
  • verifica di un singolo modulo isolatamente
  • Test di integrazione
  • verifica di corretta interazione dei moduli
  • Test di integrazione più difficile
  • comportamento da testare più complesso
  • maggiore dimensione del codice
  • spesso interazione poco/mal specificata, e moduli
    di tipo e provenienza disomogenea

Conviene prima test di unità e poi test di
integrazione (divide et impera)
27
Test di unità
  • JUnit

28
Esecuzione dei test
  • Quando si testa un programma è importante
    definire esattamente i risultati attesi (si parla
    di oracolo)
  • Si può automatizzare sia l'esecuzione dei test
    che il controllo dei risultati (Junit)
  • Junit (http//junit.org/index.htm)
  • si basa sull'idea "first testing then coding"
  • "test a little, code a little, test a little,

29
Junit esempio 1
import junit.framework.
(1) public class SimpleTest extends TestCase
(2) public SimpleTest(String name)
(3) super(name) public void
testSimpleTest() (4) int answer
2 assertEquals((11), answer) (5)
30
Spiegazioni
  • importazione delle classi definite da Junit
  • va ridefinita la classe TestCase
  • costruttore del nostro specifico test case,
  • che ha un nome (ne vedremo l'uso più avanti)
  • definizione di uno specifico test interno al
    test case
  • il test verifica che "11" produca il risultato
    definito dall'oracolo è un metodo statico della
    classe assert
  • N.B Terminologia un test case contiene uno o più
    test

31
Classi principali
  • junit.framework.TestCase
  • Consente l'esecuzione di più test, riportando
    eventuali errori
  • junit.framework.Assert
  • Insieme di metodi assert
  • Se la condizione di assert è falsa il test
    fallisce
  • junit.framework.TestSuite (vedi più avanti)
  • Collezione di test
  • Usa l'introspezione di Java per trovare tutti i
    metodi che iniziano per "test" e hanno parametri
    void
  • Il metodo run di TestSuite esegue tutti i test

32
Esempio 2
  • Test di una funzione stringStrip che
  • elimina tutte le "a" da una stringa
  • public void testStringStripFunction()
  • String expected "bb"
  • StringStripper stripper new StringStrippe
    r()
  • assertEquals(expected, stripper.stringStrip(
    "aabaaaba"))

33
Esempio 3 (1)
// Adds up a string based on the ASCII values of
its // characters and then returns the binary
representation sum public class BinString
public BinString() public String
convert(String s) return binarise(sum(s))
public int sum(String s) if(s"") return
0 if(s.length()1) return ((int)(s.charAt(0)))
return ((int)(s.charAt(0)))sum(s.substring(1)
) public String binarise(int x) if(x0)
return "" if(x21) return "1"binarise(x/2)
return "0"binarise(x/2)
34
Esempio 3 (2)
import junit.framework. public class
BinStringTest extends TestCase private
BinString binString public BinStringTest(String
name) super(name) protected void setUp()
binString new BinString() public void
testSumFunction() int expected
0 assertEquals(expected, binString.sum(""))
expected 100 assertEquals(expected,
binString.sum("d")) expected
265 assertEquals(expected, binString.sum("Add")
)
1
2
35
Esempio 3 (3)
3
public void testBinariseFunction() String
expected "101" assertEquals(expected,
binString.binarise(5)) expected
"11111100" assertEquals(expected,
binString.binarise(252)) public void
testTotalConversion() String expected
"1000001" assertEquals(expected,
binString.convert("A"))
4
36
Spiegazioni
setUp (da ridefinire) viene chiamato
automaticamente prima della valutazione di ogni
test esiste anche tearDown da ridefinire per
riportarsi in condizioni che evitino
interferenze tra test
1
test della funzione sum
2
test della funzione binarise
3
test della funzione convert
4
37
Ancora JUnit
  • Test definiti tramite luso della famiglia di
    ASSERTXXX()
  • assertTrue()
  • assertFalse()
  • assertEquals()
  • fail()
  • ...
  • È possibile eseguire una Suite di test
  • istanziare un oggetto di tipo TestSuite
  • aggiungere i test alla suite invocando il metodo
    addTest(Test) sull'oggetto istanziato

38
Nuovo esempio
public class Triangolo private int latoA,
latoB, latoC public Triangolo(int a, int b,
int c) latoA a latoB b latoC
c public boolean valido() if (latoA
0 latoB 0 latoC 0)
return false if ((latoAlatoB lt latoC)
(latoAlatoC lt latoB) (latoBlatoC lt
latoA)) return false return true

public int perimetro() if (valido())
return latoAlatoBlatoC else
return 0
39
import junit.framework. import Triangolo
public class TestTriangolo extends TestCase
private Triangolo t1,t2 public
TestTriangolo(String name) super(name)
public void setUp() t1 new
Triangolo(2,4,3) t2 new Triangolo(2,4,8)
public void testValido()
assertTrue(t1.valido()) assertFalse(t2.vali
do()) ...
40
... public void testPerimetro()
assertEquals(9,t1.perimetro())
assertEquals(0,t2.perimetro()) /public
static Test suite() TestSuite suite new
TestSuite() suite.addTest(new
TestTriangolo("testValido")) suite.addTest(new
TestTriangolo("testPerimetro")) return
suite / public static void main(String
args) junit.textui.TestRunner.run(new
TestTriangolo("testValido")) junit.textui.Test
Runner.run(new TestTriangolo("testPerimetro"))
// junit.textui.TestRunner.run(suite())
usa reflection assume che il nome del test sia
il nome del metodo del TestCase che va invocato
Si può eseguire unintera suite
textui è linterfaccia testuale, swingui è quella
grafica
41
Note
/public static Test suite() TestSuite
suite new TestSuite() suite.addTest(new
TestTriangolo("testValido")) suite.addTest(new
TestTriangolo("testPerimetro")) return
suite / Il metodo suite() costruisce una
TestSuite suite Una testSuite è una classe che
implementa Test. Dentro di sè tiene come
variabile privata un vettore di test
42
Un altro esempio
  • public class Product
  • public Product(String title, double price)
  • costruttore specializzato per la classe Product.
    Crea un prodotto come coppia di nome e prezzo
  • public String getTitle()
  • ritorna la stringa nome del prodotto
  • public double getPrice()
  • ritorna il prezzo del prodotto in virgola mobile
    su 32 bit
  • public boolean equals(Object o)
  • verifica che i prodotti siano uguali
  • instanceof se è possibile eseguire cast tra
    object o e Product allora ritorna true altrimenti
    false

43
  • Public class ShoppingCart
  • public ShoppingCart()
  • costruttore della classe ShoppingCart. Crea una
    istanza del carrello come un array di items
  • public double getBalance()
  • calcola il saldo prendendo tutti gli elementi
    dellarray (i prodotti presenti nel carrello) ed
    eseguendo la somma di tutti i prezzi
  • public void addItem(Product p)
  • aggiunge un elemento (prodotto) nellarray
    (carrello)
  • public void removeItem(Product p)
  • rimuove un elemento (prodotto) dallarray
    (carrello). Solleva una eccezione se il carrello
    è vuoto
  • public int getItemCount()
  • ritorna il numero di elementi (prodotti)
    nellarray (carrello)
  • public void empty()
  • svuota il carrello istanziando un nuovo array
  • public boolean isEmpty()
  • ritorna true se carrello vuoto (array di
    dimensione zero), false altrimenti

44
  • Public class ProductTest
  • protected void setUp()
  • protected void tearDown()
  • public ProductTest(String name)
  • public void testGetTitle()
  • assertEquals("acer travelmate",
    notebook.getTitle())
  • public void testGetPrice()
  • assertEquals(1.100, notebook.getPrice(), 0.0)
  • public void testEqualsObject()
  • assertEquals(notebook2.getPrice(),
    notebook.getPrice(), 0.0)
  • assertEquals(notebook2.getTitle(),
    notebook.getTitle())
  • assertTrue(notebook.equals(notebook))
  • assertTrue(notebook2.equals(notebook))
  • public static Test suite()

45
  • Public class ShoppingCartTest
  • public ShoppingCartTest(String name)
  • protected void setUp()
  • protected void tearDown()
  • public void testProductAdd()
  • assertEquals(expectedBalance, _bookCart.getBalance
    (), 0.0)
  • assertEquals(2, _bookCart.getItemCount())
  • public void testEmpty()
  • assertTrue(_bookCart.isEmpty())
  • public void testProductRemove()
  • assertEquals(0, _bookCart.getItemCount())
  • assertEquals(0.0, _bookCart.getBalance(), 0.0)
  • public void testProductNotFound()
  • fail(Should Raise an Exception)
  • public static Test suite()
  • Public class AllTests
  • public static Test suite()

46
Test di regressione
  • Scenario
  • programma testato con dati di test da 1 a n senza
    trovare errori
  • trovato errore con dato (n1)-simo
  • debugging e correzione del programma
  • prosecuzione del test con dato (n2)-simo
  • Probabilità non trascurabile che la correzione
    introduca errori che non lo fanno funzionare per
    qualche dato da 1 a n.

47
Test di regressione (cont.)
  • Consiste nel testare di nuovo il programma, dopo
    una modifica, con tutti i dati di test usati fino
    a quel momento, per verificare che non si ha una
    regressione
  • Necessario, ma realizzabile ed economico in
    pratica solo se il testing è almeno in parte
    automatizzato

48
Debugging (1)
  • Trovare il difetto del programma che dà origine a
    comportamento erroneo rivelato dal testing
  • Tecniche di debugging riconducibili a due tipi di
    azioni
  • identificare causa effettiva usando dati di test
    più semplici possibili
  • localizzare porzione di codice difettoso
    osservando stati intermedi della computazione
  • NB costo del debugging (spesso "contabilizzato"
    sotto la voce testing) può essere parte
    preponderante del costo di sviluppo molto
    importante sviluppare il software in modo
    sistematico per minimizzare sforzo speso in
    debugging

49
Debugging (2)
  • Debugging è attivita' difficile da rendere
    sistematica, efficienza dipende da persone ed è
    poco prevedibile, MA occorre cercare di essere
    sistematici
  • Identificare almeno uno stato corretto S1 e uno
    non corretto S2
  • Cercare di capire quali stati intermedi tra S1 e
    S2 sono corretti e quali no, fino a identificare
    uno stato corretto S1 e uno non corretto S2
    consecutivi
  • Il difetto è nellistruzione che separa S1 e S2
  • Molto utile un debugger strumento per eseguire
    programmi in modo controllato
  • breakpoint,
  • esecuzione passo-passo,
  • visualizzazione e modifica di variabili

50
Funzionalità essenziali
  • Breakpoint permettono di interrompere
    lesecuzione in un certo punto
  • Esecuzione passo passo permette di avanzare
    lesecuzione di un passo per volta
  • Esame dello stato intermedio permette di
    visualizzare il valore delle singole variabili
    (qui molto utile funzione di astrazione
    implementata con toString())
  • Modifica dello stato permette di modificare il
    valore di una o più variabili prima di riprendere
    lesecuzione
  • Oggi si usano debugger simbolici che consentono
    di operare al livello del linguaggio di
    programmazione
  • variabile variabile del linguaggio, non cella
    di memoria
  • passo istruzione del linguaggio

51
Programmazione difensiva (1)
  • Un pizzico di paranoia può essere utile scrivere
    i programmi in modo che scoprano e gestiscano
    ogni possibile situazione anomala
  • procedure chiamate con parametri attuali
    scorretti,
  • file devono essere aperti ma sono chiusi, devono
    aprirsi e non si aprono
  • riferimenti a oggetti null, array vuoti
  • Meccanismo delle eccezioni utile aiuto

52
Programmazione difensiva (2)
  • Essere scrupolosi con il test
  • ricordarsi che l'obiettivo è trovare gli errori,
    non essere contenti di non trovarne
  • testare in particolare
  • le clausole REQUIRES
  • gli invarianti di rappresentazione
  • codificare metodo repOK, testarlo allinizio di
    ogni operazione e prima di restituire i risultati
  • può convenire dare ad altri il compito di
    testare i propri programmi

53
REQUIRES o eccezioni?
  • //_at_requires x lt y
  • //_at_ensures a!null
  • //_at_ (\result ltgt (\exists int i xlti ilty
    eai ) )
  • //_at_signals (NullPointerException e) anull
  • static boolean inRange (int a, int x, int y,
    int e)
  • throws NullPointerException
  • Se chiamata di inRange scambia secondo e terzo
    parametro, implementazione diretta potrebbe non
    accorgersene e restituire false
  • durante il test aggiungere nel codice di inRange
    controllo che xlty e sollevare eccezione apposita
  • in realtà potrebbe essere vantaggioso eliminare
    REQUIRES e lasciare permanentemente eccezione

54
Controllare tutti i casi
  • Può essere molto costoso, ma va fatto quando
    possibile
  • Esempio ricevibili due soli comandi "deliver" o
    "examine" il codice
  • s Comm.receive()
  • if (s.equals("deliver")) // execute deliver
  • else if (s.equals("examine")) //execute
    examine
  • else // gestisci errore
  • Molto meglio e poco meno efficiente di
  • s Comm.receive()
  • if (s.equals("deliver")) // execute deliver
  • else //execute examine

55
Trade-offs
  • Talvolta controllo è troppo costoso se una
    procedura di ricerca binaria controlla che
    insieme di ricerca sia ordinato perde efficienza
  • Alternativa per controlli molto costosi usarli
    solo in fase di test e debugging (permettono di
    diminuire i costi della ricerca guasti) e
    toglierli (con attenzione e cautela,
    trasformandoli in commenti) quando il programma
    va in produzione
Write a Comment
User Comments (0)