Testing e Debugging.

Slides:



Advertisements
Presentazioni simili
Test delle ipotesi Il test consiste nel formulare una ipotesi (ipotesi nulla) e nel verificare se con i dati a disposizione è possibile rifiutarla o no.
Advertisements

Definitezza Vogliamo poter richiedere la “definitezza” delle funzioni
2. Introduzione alla probabilità
VALUTAZIONE La valutazione della qualità dell'istruzione consiste nella rilevazione e nell'interpretazione di informazioni relative allo svolgimento di.
I linguaggi di programmazione
Capitolo 13 Verifica e debug Lucidi relativi al volume: Java – Guida alla programmazione James Cohoon, Jack Davidson Copyright © The McGraw-Hill.
Classi ed Oggetti in Java (Cenni). Richiami Ruolo delle Classi in Java Oggetti.
Liste di Interi Esercitazione. Liste Concatenate Tipo di dato utile per memorizzare sequenze di elementi di dimensioni variabile Definizione tipicamente.
Generalità Linguaggio e Macchina Astratta
1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.
Sistemi e Tecnologie della Comunicazione
Metodi Quantitativi per Economia, Finanza e Management Lezione n° 11.
1 Defect testing Lobiettivo: scoprire difetti in un programma Un test ha successo se forza il programma a comportarsi in modo anomalo I test provano la.
Interpretazione Astratta
1 14. Verifica e Validazione Come assicurarsi che il software corrisponda alle necessità dellutente? Introdurremo i concetti di verifica e validazione.
Verification of object-oriented programs with invariants by M. Barnett, R. DeLine, M. Fähndrich, K.R.M. Leino, W. Schulte Bordignon Claudio Zampieron Elisa.
Intelligenza Artificiale 1 Gestione della conoscenza lezione 8
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Funzioni e Procedure Marco D. Santambrogio – Ver. aggiornata al 18 Aprile 2012.
CONFRONTO TRA DUE MEDIE:
Specifiche senza JML: uso delle asserzioni. 2 Asserzioni in Java Dal jdk 1.4 (da Febbraio 2002) cè meccanismo per gestire asserzioni Asserzione: espressione.
Lezione 4: Costrutti Condizionali Prof. Raffaele Montella.
Fondamenti di Informatica I a.a Il linguaggio C Il controllo di flusso La selezione condizionale Listruzione switch I cicli Le istruzioni break,
Programmazione Corso di laurea in Informatica
Primi Elementi di Programmazione in C++
Progettazione di una base di dati
Intelligenza Artificiale - AA 2001/2002 Logica formale (Parte 2) - 1 Intelligenza Artificiale Breve introduzione alla logica classica (Parte 2) Marco Piastra.
Il test di ipotesi Cuore della statistica inferenziale!
Elementi di Informatica di base
PROGRAMMARE IN C Un ambiente di sviluppo `e un software che serve per scrivere ed eseguire programmi. Generalmente integra almeno 3 funzionalita’: Editor:
Il metodo scientifico.
Le distribuzioni campionarie
1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti dispense prof. G. Levi.
Teorie e Tecniche di Psicometria
Ingegneria del software Modulo 1 -Introduzione al processo software Unità didattica 1 - Cicli di vita Ernesto Damiani Università degli Studi di Milano.
Errori casuali Si dicono casuali tutti quegli errori che possono avvenire, con la stessa probabilità, sia in difetto che in eccesso. Data questa caratteristica,
Corsa delle classi interrogazioni Prove di ingresso Rispetto delle regole controlli Ogni dito un senso classifica.
Corsa delle classi interrogazioni Prove di ingresso Rispetto delle regole controlli Ogni dito un senso classifica.
Cap. 15 Caso, probabilità e variabili casuali Cioè gli ingredienti matematici per fare buona inferenza statistica.
La verifica del software
Sessione live Testing. Esercizio Quesito 1 Soluzione 1.
Anno accademico Le istruzioni di controllo in C.
Definizione(i) di Ingegneria del Software (IEEE) Strategie sistematiche, a partire da richieste formulate del committente, per lo sviluppo, esercizio e.
Sistemi e Tecnologie Informatiche Verifica di correttezza di un programma.
Algebra di Boole.
Calcolo letterale.
Logica F. orilia. Lezz Lunedì 4 Novembre 2013.
Il Linguaggio SQL. Le interrogazioni in SQL (continua…) La parte di SQL dedicata alla formulazione di interrogazioni fa parte del DML. SQL esprime le.
Fondamenti di Informatica II Ingegneria Informatica (A-I) Prof. M.T. PAZIENZA a.a – 3° ciclo.
LABORATORIO DI INFORMATICA Ingegneria Informatica a. a
13 ottobre Decisioni F. Bombi 13 ottobre 2002.
Ingegneria del software Modulo 1 -Introduzione al processo software Unità didattica 5 – Test e verifica Ernesto Damiani Università degli Studi di Milano.
Ripasso su Java. Introduzione Per risolvere problemi complessi, i linguaggi di programmazione forniscono costrutti per realizzare nuove funzioni che trasformino.
Specifiche. Scopo e significato delle specifiche (1) Lo scopo di una specifica è di definire il comportamento di un ’ astrazione. Gli utenti si baseranno.
ALLOCAZIONE STATICA: LIMITI Per quanto sappiamo finora, in C le variabili sono sempre dichiarate staticamente –la loro esistenza deve essere prevista e.
Progettazione di una base di dati Ciclo di vita di un sistema informativo Studio di fattibilità definisce le varie alternative possibili, i relativi costi.
1 Laboratorio di Introduzione alla Programmazione §II MODULO §3 crediti §Esame e voto unico (su 6 crediti totali)
Ingegneria del software Modulo 1 -Introduzione al processo software Unità didattica 5 -Test e verifica Ernesto Damiani Università degli Studi di Milano.
Esercitazione del 9 marzo 2007 Ereditarieta’. Richiami Definire sottoclassi (ereditarieta’) Overriding Specificatori di accesso (private, protected) Principio.
Ingegneria del software Modulo 1 - Introduzione al processo software Unità didattica 5 -Test e verifica Ernesto Damiani Università degli Studi di Milano.
Progettazione di basi di dati: metodologie e modelli
LIP: 15 Marzo 2005 Vettori di interi. Esercizio proposto Definire una classe VectorInt i cui oggetti sono vettori omogenei di interi ordinati in modo.
Metodologia della ricerca e analisi dei dati in (psico)linguistica 24 Giugno 2015 Statistica inferenziale
1 Saggio breve argomentativo. 2 Sulle sue tracce… Definizione di saggio filosofico Come si costruisce un’argomentazione Metodo per realizzare un saggio:
Testing e Debugging La verifica del software. Perché? Che cosa? Quando? GOAL: software con zero difetti … MA impossibile da ottenere e garantire Necessaria.
IL PROCESSO SOFTWARE EMERSO DALLA DOCUMENTAZIONE.
1 DISTRIBUZIONI DI PROBABILITÁ. 2 distribu- zione che permette di calcolare le probabilità degli eventi possibili A tutte le variabili casuali, discrete.
Logica Lezione 8, DISTRIBUIRE COMPITO 1.
Eccezioni in Java. Le eccezioni in Java Exception handling: insieme di costrutti e regole sintattiche e semantiche presenti nel linguaggio allo scopo.
INTRODUZIONE ALL’ANALISI DELLA VARIANZA
Logica Lezione 11, Annuncio Non si terrà la lezione di Lunedì 16 Marzo.
Transcript della presentazione:

Testing e Debugging

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!

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

Attenzione: terminologia non standardizzata! 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!

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

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

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

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?

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

Test funzionale: combinazioni proposizionali (1) ESEMPIO Combinare i vari casi alternativi espressi da una specifica static boolean isPrime (int x) //@ensures (* \result <==> x è primo*) Scegliere dati di test primi e non primi. Es. 5 e 8

Test funzionale: combinazioni proposizionali (2) In generale, possiamo identificare le parti in alternativa di una specifica espressa come formula di logica proposizionale usando l’operatore || static float sqrt (float x, float epsilon) //@requires x >= 0 && .00001 < epsilon < .001 ; //@ensures x-epsilon <= \result * \result <= x+epsilon ; REQUIRES congiunzione di x >= 0 con .00001<epsilon<.001 Parte x >= 0 equivalente a x=0 || x>0 Combinazioni ottenibili: x = 0 && .00001 < epsilon < .001 x > 0 && .00001 < epsilon < .001

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 x1&&x2&&…. || y1&&y2&&… ym || … NB: metodo applicabile a clausole requires ma non sempre a clausola ensures in questo caso non conoscendo codice impossibile prevedere se \result*\result <x o =x o >x: come scegliere x?

Es. applicato a effects Es. static int maxOfThree (int x, int y, int z) { //@ensures (* restituisce il valore massimo fra x, y, z *) Ci sono tre alternative: il massimo è x, è y, o è z //@ ensures \result == x && x>=y && x>=z || \result == y && y>=x && y>=z || \result == z && z>=x && z>=y 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)

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

è bene evitare la requires e definire funzioni totali Casi eccezionali Testare non solo il comportamento normale ma anche le eccezioni //@ensures a!=null && //@ (\exists int i; 0<=i && i<a.length; x==a[i]) && a[\result]==x ; //@ signals (NotFoundException e) //@ (\forall int i; 0<=i && i<a.length; x != a[i]); //@ 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

Testing con valori limite (boundary values) Se valore dell’input può stare in un intervallo, testare estremi dell’intervallo 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

Altri esempi Triangoli identificati da vertici: tre punti allineati due punti coincidenti tre punti coincidenti triangolo rettangolo un vertice nell’origine o sugli assi ….

Esempio Casi limite: //@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 x=y !=z ecc.

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 //@ensures(* removes all elements of v2 and appends //@them in reverse order to the end of v1 *) static void appendVector(Vector v1, Vector v2){ while (v2.size() > 0) { v1.addElement(v2.lastElement()); v2.removeElementAt(v2.size()-1); } } NON è vietato che v1 e v2 siano lo stesso Vector: testando questo caso si trova un errore

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: l’astrazione IntSet

Specifica di IntSet public class IntSet { /*OVERVIEW: insiemi di interi illimitati e modificabili; per es.: {1, 2, 10, -55} */ //costruttori: //@ensures (\forall int y;;!this.isIn(y)); public IntSet(){ } //metodi mutators: //@ ensures this.isIn(x) && (\forall int y; x!=y; //@ \old(this.isIn(y)) <==> this.isIn(y)); public void insert(int x){ } //@ ensures !this.isIn(x) && (\forall int y; x!=y; public void remove(int x){ } //@ensures (*\result è true sse x è fra gli elementi di this*); public boolean isIn (int x){} //@ ensures (*\result è cardinalità di this *); public int size(){}

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

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 qual’e la causa dell’errore. 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

Esempio: semplificare post Verifica della postcondizione di insert è difficile perche’ richiede di verificare che tutti gli elementi già presenti nell’insieme non sono eliminati Piu’ facile verificare solo che la cardinalità dell’insieme sia corretta: isIn(x) && (\old(isIn(x))==> size(x)==\old(size(x)) || \old(isIn(x))==>size(x)==1+\old(size(x)))

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

Test di unità e di integrazione 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)

Test di unità JUnit

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, …

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((1+1), answer); (5)

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 "1+1" produca il risultato definito dall'oracolo; è un metodo statico della classe assert N.B Terminologia: un test case contiene uno o più test

Classi principali junit.framework.TestCase junit.framework.Assert 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

Esempio 2 Test di una funzione stringStrip che elimina tutte le "a" da una stringa public void testStringStripFunction() { String expected = "bb" StringStripper stripper = new StringStripper(); assertEquals(expected, stripper.stringStrip("aabaaaba")); }

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(x==0) return ""; if(x%2==1) return "1"+binarise(x/2); return "0"+binarise(x/2);

Esempio 3 (2) 1 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

Esempio 3 (3) 3 4 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

Spiegazioni setUp (da ridefinire) viene chiamato automaticamente 1 prima della valutazione di ogni test; esiste anche tearDown da ridefinire per riportarsi in condizioni che evitino interferenze tra test 1 2 test della funzione sum 3 test della funzione binarise 4 test della funzione convert

Ancora JUnit Test definiti tramite l’uso 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

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 ((latoA+latoB < latoC) || (latoA+latoC < latoB) || (latoB+latoC < latoA)) return true; } public int perimetro() { if (valido()) return latoA+latoB+latoC; else return 0; } }

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.valido()); } ...

public void testPerimetro() { assertEquals(9,t1.perimetro()); ... 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.TestRunner.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 un’intera suite textui è l’interfaccia testuale, swingui è quella grafica

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

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

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 dell’array (i prodotti presenti nel carrello) ed eseguendo la somma di tutti i prezzi public void addItem(Product p) aggiunge un elemento (prodotto) nell’array (carrello) public void removeItem(Product p) rimuove un elemento (prodotto) dall’array (carrello). Solleva una eccezione se il carrello è vuoto public int getItemCount() ritorna il numero di elementi (prodotti) nell’array (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

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()

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

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

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

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

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 S’1 e uno non corretto S’2 consecutivi Il difetto è nell’istruzione che separa S’1 e S’2 Molto utile un debugger: strumento per eseguire programmi in modo controllato: breakpoint, esecuzione passo-passo, visualizzazione e modifica di variabili

Funzionalità essenziali Breakpoint: permettono di interrompere l’esecuzione in un certo punto Esecuzione passo passo: permette di avanzare l’esecuzione 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 l’esecuzione 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

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

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 all’inizio di ogni operazione e prima di restituire i risultati può convenire dare ad altri il compito di testare i propri programmi

REQUIRES o eccezioni? //@requires x <= y //@ensures a!=null && //@ (\result <==> (\exists int i; x<=i && i<=y; e==a[i] ) ) //@signals (NullPointerException e) a==null 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 x<=y e sollevare eccezione apposita in realtà potrebbe essere vantaggioso eliminare REQUIRES e lasciare permanentemente eccezione

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 else { //execute examine }

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