Collaudo e Collaudo e Correzione di Errori Correzione di Errori
C. Horstmann
C. Horstmann
Fondamenti di programmazione e Java 2
Fondamenti di programmazione e Java 2
3^ edizione Apogeo
3^ edizione Apogeo
trad. Nicola Fanizzi
trad. Nicola Fanizzi
corso di Programmazione, CdS: Informatica TPS
corso di Programmazione, CdS: Informatica TPS
Dip. di Informatica, Università degli studi di Bari
Dip. di Informatica, Università degli studi di Bari
2 2
Obiettivi Obiettivi
Imparare ad eseguire il collaudo di unità Imparare ad eseguire il collaudo di unità
Capire i principi della selezione e valutazione dei Capire i principi della selezione e valutazione dei casi di test
casi di test
Imparare ad usare il logging Imparare ad usare il logging
Prendere familiarità con l'udo di un debugger Prendere familiarità con l'udo di un debugger
Imparare le strategie per un debugging efficace Imparare le strategie per un debugging efficace
3 3
Collaudo di Unità Collaudo di Unità
Unico e più importante strumento per il collaudo Unico e più importante strumento per il collaudo
Controlla un singolo metodo o un insieme di metodi Controlla un singolo metodo o un insieme di metodi cooperanti
cooperanti
Non si testa il programma completo che si sta Non si testa il programma completo che si sta sviluppando, bensì le singole classi isolate
sviluppando, bensì le singole classi isolate
Per ogni test, si fornisce una semplice classe detta Per ogni test, si fornisce una semplice classe detta test harness
test harness
La classe La classe test harness test harness procura i parametri ai metodi procura i parametri ai metodi da collaudare
da collaudare
4 4
Esempio:
Esempio:
Impostare Classi Test Harness Impostare Classi Test Harness
Usiamo un comune algoritmo per il calcolo della Usiamo un comune algoritmo per il calcolo della radice quadrata:
radice quadrata:
Dare un valore x che potrebbe essere abbastanza vicino Dare un valore x che potrebbe essere abbastanza vicino alla radice quadrata desiderata (x = a va bene)
alla radice quadrata desiderata (x = a va bene)
La vera radice sta tra x e a/x La vera radice sta tra x e a/x
Considerare il punto medio (x + a/x) / 2 come migliore Considerare il punto medio (x + a/x) / 2 come migliore approssimazione
approssimazione
Ripetere la procedura. Fermarsi quando due Ripetere la procedura. Fermarsi quando due
approssimazioni successive sono tra loro molto vicine
approssimazioni successive sono tra loro molto vicine
5 5
Esempio:
Esempio:
Impostare Classi Test Harness Impostare Classi Test Harness
Il metodo converge rapidamente Il metodo converge rapidamente Radice quadrata di 100:
Radice quadrata di 100:
Guess #1: 50.5 Guess #1: 50.5 Guess #2:
Guess #2:
26.24009900990099 26.24009900990099 Guess #3:
Guess #3:
15.025530119986813 15.025530119986813 Guess #4:
Guess #4:
10.840434673026925 10.840434673026925 Guess #5:
Guess #5:
10.032578510960604 10.032578510960604 Guess #6:
Guess #6:
10.000052895642693 10.000052895642693 Guess #7:
Guess #7:
10.000000000139897
10.000000000139897
6 6
RootApproximator.java RootApproximator.java
01: /**
01: /**
02: Calcola approssimazioni della radice quadrata 02: Calcola approssimazioni della radice quadrata 03: di un numero usando l'algoritmo di Erone.
03: di un numero usando l'algoritmo di Erone.
04: */
04: */
05: public class RootApproximator 05: public class RootApproximator 06: {
06: {
07: /**
07: /**
08: Costruisce l'approssimazione della radice 08: Costruisce l'approssimazione della radice per un dato numero.
per un dato numero.
09: @param aNumber numero per il quale estrarre 09: @param aNumber numero per il quale estrarre la radice quadrata
la radice quadrata
10: (Precondizione: aNumber >= 0) 10: (Precondizione: aNumber >= 0) 11: */
11: */
12: public RootApproximator(double aNumber) 12: public RootApproximator(double aNumber) 13: {
13: {
14: a = aNumber;
14: a = aNumber;
15: xold = 1;
15: xold = 1;
16: xnew = a;
16: xnew = a;
7 7
RootApproximator.java RootApproximator.java
18: 18:
19: /**
19: /**
20: Calcola una approssimazione migliore della corrente.
20: Calcola una approssimazione migliore della corrente.
21: @return the next guess 21: @return the next guess 22: */
22: */
23: public double nextGuess() 23: public double nextGuess() 24: {
24: {
25: xold = xnew;
25: xold = xnew;
26: if (xold != 0) 26: if (xold != 0)
27: xnew = (xold + a / xold) / 2;
27: xnew = (xold + a / xold) / 2;
28: return xnew;
28: return xnew;
29: }
29: }
30: 30:
8 8
RootApproximator.java RootApproximator.java
31: /**
31: /**
32: Calcola la radice migliorando l'approssimazione 32: Calcola la radice migliorando l'approssimazione 33: finchè due successivi risultati risultano
33: finchè due successivi risultati risultano approssimativamente uguali.
approssimativamente uguali.
34: @return il valore della radice quadrata calcolato 34: @return il valore della radice quadrata calcolato 35: */
35: */
36: public double getRoot() 36: public double getRoot() 37: {
37: {
38: assert a >= 0;
38: assert a >= 0;
39: while (!Numeric.approxEqual(xnew, xold)) 39: while (!Numeric.approxEqual(xnew, xold)) 40: nextGuess();
40: nextGuess();
41: return xnew;
41: return xnew;
42: } 42: } 43:
43:
44: private double a; // il numero di cui si deve 44: private double a; // il numero di cui si deve calcolare la radice
calcolare la radice
45: private double xnew; // approssimazione corrente 45: private double xnew; // approssimazione corrente 46: private double xold; // approssimazione precedente 46: private double xold; // approssimazione precedente 47: }
47: }
9 9
Numeric.java Numeric.java
01: /**
01: /**
02: A class for useful numeric methods.
02: A class for useful numeric methods.
03: */
03: */
04: public class Numeric 04: public class Numeric 05: {
05: { 06: /**
06: /**
07: Tests whether two floating-point numbers are.
07: Tests whether two floating-point numbers are.
08: equal, except for a roundoff error 08: equal, except for a roundoff error 09: @param x a floating-point number 09: @param x a floating-point number 10: @param y a floating-point number 10: @param y a floating-point number
11: @return true if x and y are approximately equal 11: @return true if x and y are approximately equal 12: */
12: */
13: public static boolean approxEqual(double x, double y) 13: public static boolean approxEqual(double x, double y) 14: {
14: {
15: final double EPSILON = 1E-12;
15: final double EPSILON = 1E-12;
16: return Math.abs(x - y) <= EPSILON;
16: return Math.abs(x - y) <= EPSILON;
17: }
17: }
18: }
18: }
10 10
RootApproximatorTester RootApproximatorTester .java .java
01: import java.util.Scanner;
01: import java.util.Scanner;
02: 02:
03: /**
03: /**
04: This program prints ten approximations for a square 04: This program prints ten approximations for a square root.
root.
05: */
05: */
06: public class RootApproximatorTester 06: public class RootApproximatorTester 07: {
07: {
08: public static void main(String[] args) 08: public static void main(String[] args) 09: {
09: {
10: System.out.print("Enter a number: ");
10: System.out.print("Enter a number: ");
11: Scanner in = new Scanner(System.in);
11: Scanner in = new Scanner(System.in);
12: double x = in.nextDouble();
12: double x = in.nextDouble();
13: RootApproximator r = new RootApproximator(x);
13: RootApproximator r = new RootApproximator(x);
14: final int MAX_TRIES = 10;
14: final int MAX_TRIES = 10;
11 11
RootApproximatorTester RootApproximatorTester .java .java
15: for (int tries = 1; tries <= MAX_TRIES; tries++) 15: for (int tries = 1; tries <= MAX_TRIES; tries++) 16: {
16: {
17: double y = r.nextGuess();
17: double y = r.nextGuess();
18: System.out.println("Guess #" + tries + ": " + y);
18: System.out.println("Guess #" + tries + ": " + y);
19: } 19: }
20: System.out.println("Square root: " + r.getRoot());
20: System.out.println("Square root: " + r.getRoot());
21: }
21: }
22: }
22: }
12 12
Collaudare il Programma Collaudare il Programma
Output Output
Enter a number: 100 Enter a number: 100 Guess #1: 50.5
Guess #1: 50.5
Guess #2: 26.24009900990099 Guess #2: 26.24009900990099 Guess #3: 15.025530119986813 Guess #3: 15.025530119986813 Guess #4: 10.840434673026925 Guess #4: 10.840434673026925 Guess #5: 10.032578510960604 Guess #5: 10.032578510960604 Guess #6: 10.000052895642693 Guess #6: 10.000052895642693 Guess #7: 10.000000000139897 Guess #7: 10.000000000139897 Guess #8: 10.0
Guess #8: 10.0
Guess #9: 10.0
Guess #9: 10.0
Guess #10: 10.0
Guess #10: 10.0
Square root: 10.0
Square root: 10.0
13 13
Collaudare il Programma Collaudare il Programma
La classe La classe RootApproximator RootApproximator funziona funziona correttamente per tutti gli input ?
correttamente per tutti gli input ?
Serve collaudarla su tanti altri valori Serve collaudarla su tanti altri valori
Continuare a ri-collaudare con altri valori non è una Continuare a ri-collaudare con altri valori non è una buona idea; i collaudi non sono ripetibili
buona idea; i collaudi non sono ripetibili
Se un problema viene corretto e si deve collaudare di Se un problema viene corretto e si deve collaudare di nuovo, si dovrebbero ricordare tutti gli input precedenti nuovo, si dovrebbero ricordare tutti gli input precedenti
Soluzione Soluzione : scrivere : scrivere test harness test harness che facilitano la che facilitano la ripetizione del collaudo delle unità
ripetizione del collaudo delle unità
14 14
Fornire l'Input per il Collaudo Fornire l'Input per il Collaudo
Ci sono vari meccanismi per fornire casi di test Ci sono vari meccanismi per fornire casi di test
Un meccanismo è quello di cablare (scrivere direttamente Un meccanismo è quello di cablare (scrivere direttamente nel codice) gli inputs di test nel test harness
nel codice) gli inputs di test nel test harness
Si esegue semplicemente il test harness ogni volta che si Si esegue semplicemente il test harness ogni volta che si corregge un bug nella classe collaudata
corregge un bug nella classe collaudata
Alternativa Alternativa : si tengono gli input di test su file appositi : si tengono gli input di test su file appositi
15 15
RootApproximatorHarness1 RootApproximatorHarness1
.java .java
01: /**
01: /**
02: Programma che calcola le radici quadrate di valori di 02: Programma che calcola le radici quadrate di valori di input selezionati
input selezionati 03: */
03: */
04: public class RootApproximatorHarness1 04: public class RootApproximatorHarness1 05: {
05: {
06: public static void main(String[] args) 06: public static void main(String[] args) 07: {
07: {
08: double[] testInputs = { 100, 4, 2, 1, 0.25, 0.01 };
08: double[] testInputs = { 100, 4, 2, 1, 0.25, 0.01 };
09: for (double x : testInputs) 09: for (double x : testInputs) 10: {
10: {
11: RootApproximator r = new RootApproximator(x);
11: RootApproximator r = new RootApproximator(x);
12: double y = r.getRoot();
12: double y = r.getRoot();
13: System.out.println("square root of " + x 13: System.out.println("square root of " + x 14: + " = " + y);
14: + " = " + y);
15: }
15: }
16: }
16: }
17: }
17: }
16 16
RootApproximatorHarness1 RootApproximatorHarness1
.java .java
Output Output
square root of 100.0 = 10.0 square root of 100.0 = 10.0 square root of 4.0 = 2.0
square root of 4.0 = 2.0
square root of 2.0 = 1.414213562373095 square root of 2.0 = 1.414213562373095 square root of 1.0 = 1.0
square root of 1.0 = 1.0
square root of 0.25 = 0.5
square root of 0.25 = 0.5
square root of 0.01 = 0.1
square root of 0.01 = 0.1
17 17
Fornire l'Input per il Collaudo Fornire l'Input per il Collaudo
Si possono anche generare casi di test in maniera Si possono anche generare casi di test in maniera automatica
automatica
Se sono possibili pochi input Se sono possibili pochi input
allora basta far girare il test per intero allora basta far girare il test per intero
ciclando su un numero (rappresentativo) di casi
ciclando su un numero (rappresentativo) di casi
18 18
RootApproximatorHarness2 RootApproximatorHarness2
.java .java
01: /**
01: /**
02: Programma che calcola le radici quadrate 02: Programma che calcola le radici quadrate
03: di valori in input forniti attraverso un ciclo.
03: di valori in input forniti attraverso un ciclo.
04: */
04: */
05: public class RootApproximatorHarness2 05: public class RootApproximatorHarness2 06: {
06: {
07: public static void main(String[] args) 07: public static void main(String[] args) 08: {
08: {
09: final double MIN = 1;
09: final double MIN = 1;
10: final double MAX = 10;
10: final double MAX = 10;
11: final double INCREMENT = 0.5;
11: final double INCREMENT = 0.5;
12: for (double x = MIN; x <= MAX; x = x + INCREMENT) 12: for (double x = MIN; x <= MAX; x = x + INCREMENT) 13: {
13: {
14: RootApproximator r = new RootApproximator(x);
14: RootApproximator r = new RootApproximator(x);
15: double y = r.getRoot();
15: double y = r.getRoot();
16: System.out.println("square root of " + x 16: System.out.println("square root of " + x 17: + " = " + y);
17: + " = " + y);
18: }
18: }
19: }
19: }
20: }
20: }
19 19
RootApproximatorHarness2 RootApproximatorHarness2
.java .java
Output Output
square root of 1.0 = 1.0 square root of 1.0 = 1.0
square root of 1.5 = 1.224744871391589 square root of 1.5 = 1.224744871391589 square root of 2.0 = 1.414213562373095 square root of 2.0 = 1.414213562373095 . . .
. . .
square root of 9.0 = 3.0 square root of 9.0 = 3.0
square root of 9.5 = 3.0822070014844885
square root of 9.5 = 3.0822070014844885
square root of 10.0 = 3.162277660168379
square root of 10.0 = 3.162277660168379
20 20
Fornire l'Input per il Collaudo Fornire l'Input per il Collaudo
Test precedenti hanno ristreto il campo ad un limitato Test precedenti hanno ristreto il campo ad un limitato sotto-insieme di valori
sotto-insieme di valori
Alternativa Alternativa : generazione causale di casi di test : generazione causale di casi di test
21 21
RootApproximatorHarness3 RootApproximatorHarness3
.java .java
01: import java.util.Random;
01: import java.util.Random;
02: 02:
03: /**
03: /**
04: Il programma calcola la radice quadrata di numeri 04: Il programma calcola la radice quadrata di numeri casuali
casuali 05: */
05: */
06: public class RootApproximatorHarness3 06: public class RootApproximatorHarness3 07: {
07: {
08: public static void main(String[] args) 08: public static void main(String[] args) 09: {
09: {
10: final double SAMPLES = 100;
10: final double SAMPLES = 100;
11: Random generator = new Random();
11: Random generator = new Random();
12: for (int i = 1; i <= SAMPLES; i++) 12: for (int i = 1; i <= SAMPLES; i++) 13: {
13: {
14: // Genera un valore casuale di test
14: // Genera un valore casuale di test
15: 15:
22 22
RootApproximatorHarness3 RootApproximatorHarness3
.java .java
16: double x = 1000 * generator.nextDouble();
16: double x = 1000 * generator.nextDouble();
17: RootApproximator r = new RootApproximator(x);
17: RootApproximator r = new RootApproximator(x);
18: double y = r.getRoot();
18: double y = r.getRoot();
19: System.out.println("square root of " + x 19: System.out.println("square root of " + x 20: + " = " + y);
20: + " = " + y);
21: }
21: }
22: }
22: }
23: }
23: }
23 23
RootApproximatorHarness3 RootApproximatorHarness3
.java .java
Output Output
square root of 810.4079626570873 = 28.467665212607223 square root of 810.4079626570873 = 28.467665212607223 square root of 480.50291114306344 = 21.9203766195534 square root of 480.50291114306344 = 21.9203766195534 square root of 643.5463246844379 = 25.36821485017103 square root of 643.5463246844379 = 25.36821485017103 square root of 506.5708496713842 = 22.507128863348704 square root of 506.5708496713842 = 22.507128863348704 square root of 539.6401504334708 = 23.230156057019308 square root of 539.6401504334708 = 23.230156057019308 square root of 795.0220214851004 = 28.196134867834285 square root of 795.0220214851004 = 28.196134867834285 . . .
. . .
24 24
Fornire l'Input per il Collaudo Fornire l'Input per il Collaudo
Selezionare buoni casi di test è un'abilità nel Selezionare buoni casi di test è un'abilità nel debuggare i programmi
debuggare i programmi
Testare tutte le caratteristiche dei metodi che si Testare tutte le caratteristiche dei metodi che si stanno collaudando
stanno collaudando
Testare i casi tipici Testare i casi tipici
100, 1/4, 0.01, 2, 10E12, per 100, 1/4, 0.01, 2, 10E12, per
SquareRootApproximator SquareRootApproximator
Testare i casi limite: Testare i casi limite:
casi che sono ai confini del dominio di input casi che sono ai confini del dominio di input
0 per
0 per SquareRootApproximator SquareRootApproximator
25 25
Fornire l'Input per il Collaudo Fornire l'Input per il Collaudo
I programmatori spesso fanno errori trattando I programmatori spesso fanno errori trattando condizioni limite:
condizioni limite:
divisione per zero, estrarre caratteri da stringhe vuote, e divisione per zero, estrarre caratteri da stringhe vuote, e accedere riferimenti nulli
accedere riferimenti nulli
Racogliere casi di test negativi: gli input che ci si Racogliere casi di test negativi: gli input che ci si aspetta che il programma rigetti
aspetta che il programma rigetti
Esempio: la radice di -2. Esempio: la radice di -2.
Il test è superato se l'harness termina con Il test è superato se l'harness termina con l'asserzione del fallimento
l'asserzione del fallimento
(se il controllo delle asserzioni è abilitato)
(se il controllo delle asserzioni è abilitato)
26 26
Lettura di Input di test da File Lettura di Input di test da File
Più elegante mettere i valori di test in un file Più elegante mettere i valori di test in un file
Ridirezione dell'Input: Ridirezione dell'Input:
Alcune IDE non supportano la ridirezione dell'input. Alcune IDE non supportano la ridirezione dell'input.
Allora si usa la shell (console con riga di comando) Allora si usa la shell (console con riga di comando)
Ridirezione dell'output: Ridirezione dell'output:
java Program < data.txt java Program < data.txt
java Program > output.txt
java Program > output.txt
27 27
RootApproximatorHarness4 RootApproximatorHarness4
.java .java
01: import java.util.Scanner;
01: import java.util.Scanner;
02: 02:
03: /**
03: /**
04: Il programma calcola la radice quadrata di input 04: Il programma calcola la radice quadrata di input 05: forniti attraverso System.in.
05: forniti attraverso System.in.
06: */
06: */
07: public class RootApproximatorHarness4 07: public class RootApproximatorHarness4 08: {
08: {
09: public static void main(String[] args) 09: public static void main(String[] args) 10: {
10: {
11: Scanner in = new Scanner(System.in);
11: Scanner in = new Scanner(System.in);
12: boolean done = false;
12: boolean done = false;
13: while (in.hasNextDouble()) 13: while (in.hasNextDouble()) 14: {
14: {
15: double x = in.nextDouble();
15: double x = in.nextDouble();
16: RootApproximator r = new RootApproximator(x);
16: RootApproximator r = new RootApproximator(x);
17: double y = r.getRoot();
17: double y = r.getRoot();
18:
18:
19: System.out.println("square root of " + x 19: System.out.println("square root of " + x 20: + " = " + y);
20: + " = " + y);
28 28
Lettura di Input di test da File Lettura di Input di test da File
File File test.in test.in : :
facendo girare il programma:
facendo girare il programma:
1 100 1 100 2 4 2 4 3 2 3 2 4 1 4 1
5 0.25 5 0.25 6 0.01 6 0.01
java RootApproximatorHarness4 < test.in > test.out
java RootApproximatorHarness4 < test.in > test.out
29 29
Lettura di Input di test da File Lettura di Input di test da File
File File test.out test.out : :
1 square root of 100.0 = 10.0 1 square root of 100.0 = 10.0 2 square root of 4.0 = 2.0
2 square root of 4.0 = 2.0
3 square root of 2.0 = 1.414213562373095 3 square root of 2.0 = 1.414213562373095 4 square root of 1.0 = 1.0
4 square root of 1.0 = 1.0
5 square root of 0.25 = 0.5
5 square root of 0.25 = 0.5
6 square root of 0.01 = 0.1
6 square root of 0.01 = 0.1
30 30
Valutazione dei Casi di Test Valutazione dei Casi di Test
Come saper se il l'output è corretto ? Come saper se il l'output è corretto ?
Calcola re valori corretti a mano Calcola re valori corretti a mano
es. per un programma di pagamenti, es. per un programma di pagamenti, calcolare le tasse manualmente
calcolare le tasse manualmente
Fornire input di test per i quali si sa la risposta Fornire input di test per i quali si sa la risposta
es. la radice di 4 è 2 e la radice di 100 è 10 es. la radice di 4 è 2 e la radice di 100 è 10
31 31
Valutazione dei Casi di Test Valutazione dei Casi di Test
Verificare che i valori di output soddisfi cete proprietà Verificare che i valori di output soddisfi cete proprietà
es. radice quadrata elevata al quadrato = valore originario es. radice quadrata elevata al quadrato = valore originario
Usare un Oracolo: un metodo lento ma affidabile per Usare un Oracolo: un metodo lento ma affidabile per calcolare un risulktato per esigenze di collaudo
calcolare un risulktato per esigenze di collaudo
es. usare es. usare Math.pow Math.pow per calcolare più lentamernte x per calcolare più lentamernte x
1/21/2(equivalente alla radice quadrata di x)
(equivalente alla radice quadrata di x)
32 32
RootApproximatorHarness5 RootApproximatorHarness5
.java .java
01: import java.util.Random;
01: import java.util.Random;
02: 02:
03: /**
03: /**
04: Il programma verifica il calcolo dei valori della radice 04: Il programma verifica il calcolo dei valori della radice 05: quadrata controllando una proprietà delle radici quad.
05: quadrata controllando una proprietà delle radici quad.
06: */
06: */
07: public class RootApproximatorHarness5 07: public class RootApproximatorHarness5 08: {
08: {
09: public static void main(String[] args) 09: public static void main(String[] args) 10: {
10: {
11: final double SAMPLES = 100;
11: final double SAMPLES = 100;
12: int passcount = 0;
12: int passcount = 0;
13: int failcount = 0;
13: int failcount = 0;
14: Random generator = new Random();
14: Random generator = new Random();
15: for (int i = 1; i <= SAMPLES; i++) 15: for (int i = 1; i <= SAMPLES; i++) 16: {
16: {
33 33
RootApproximatorHarness5 RootApproximatorHarness5
.java .java
17: // Generare valori di test casuali 17: // Generare valori di test casuali 18: 18:
19: double x = 1000 * generator.nextDouble();
19: double x = 1000 * generator.nextDouble();
20: RootApproximator r = new RootApproximator(x);
20: RootApproximator r = new RootApproximator(x);
21: double y = r.getRoot();
21: double y = r.getRoot();
22: 22:
23: // controlla che il valore di test soddisfi la 23: // controlla che il valore di test soddisfi la proprietà del quadrato
proprietà del quadrato 24: 24:
25: if (Numeric.approxEqual(y * y, x)) 25: if (Numeric.approxEqual(y * y, x)) 26: {
26: {
27: System.out.print("Test passed: ");
27: System.out.print("Test passed: ");
28: passcount++;
28: passcount++;
29: } 29: } 30: else 30: else 31: { 31: {
32: System.out.print("Test failed: ");
32: System.out.print("Test failed: ");
33: failcount++;
33: failcount++;
34: }
34: }
34 34
RootApproximatorHarness5 RootApproximatorHarness5
.java .java
35: 35:
36: System.out.println("x = " + x 36: System.out.println("x = " + x
37: + ", root squared = " + y * y);
37: + ", root squared = " + y * y);
38: } 38: }
39: System.out.println("Pass: " + passcount);
39: System.out.println("Pass: " + passcount);
40: System.out.println("Fail: " + failcount);
40: System.out.println("Fail: " + failcount);
41: }
41: }
42: }
42: }
35 35
RootApproximatorHarness5 RootApproximatorHarness5
.java .java
Output Output
Test passed: x = 913.6505141736327, root squared = 913.6505141736328 Test passed: x = 913.6505141736327, root squared = 913.6505141736328 Test passed: x = 810.4959723987972, root squared = 810.4959723987972 Test passed: x = 810.4959723987972, root squared = 810.4959723987972 Test passed: x = 503.84630929985883, root squared = 503.8463092998589 Test passed: x = 503.84630929985883, root squared = 503.8463092998589 Test passed: x = 115.4885096006315, root squared = 115.48850960063153 Test passed: x = 115.4885096006315, root squared = 115.48850960063153 Test passed: x = 384.973238438713, root squared = 384.973238438713 Test passed: x = 384.973238438713, root squared = 384.973238438713 . . .
. . .
Pass: 100 Pass: 100 Fail: 0 Fail: 0
36 36
RootApproximatorHarness6 RootApproximatorHarness6
.java .java
01: import java.util.Random;
01: import java.util.Random;
02: 02:
03: /**
03: /**
04: Il programma verifica il calcolo dei valori della radice 04: Il programma verifica il calcolo dei valori della radice 05: quadrata mediante un oracolo.
05: quadrata mediante un oracolo.
06: */
06: */
07: public class RootApproximatorHarness6 07: public class RootApproximatorHarness6 08: {
08: {
09: public static void main(String[] args) 09: public static void main(String[] args) 10: {
10: {
11: final double SAMPLES = 100;
11: final double SAMPLES = 100;
12: int passcount = 0;
12: int passcount = 0;
13: int failcount = 0;
13: int failcount = 0;
14: Random generator = new Random();
14: Random generator = new Random();
15: for (int i = 1; i <= SAMPLES; i++) 15: for (int i = 1; i <= SAMPLES; i++) 16: {
16: {
17: // Generare valori di test casuali
17: // Generare valori di test casuali
18: 18:
37 37
RootApproximatorHarness6 RootApproximatorHarness6
.java .java
19: double x = 1000 * generator.nextDouble();
19: double x = 1000 * generator.nextDouble();
20: RootApproximator r = new RootApproximator(x);
20: RootApproximator r = new RootApproximator(x);
21: double y = r.getRoot();
21: double y = r.getRoot();
22: 22:
23: double oracleValue = Math.pow(x, 0.5);
23: double oracleValue = Math.pow(x, 0.5);
24: 24:
25: // controllare che il valore di test eguagli 25: // controllare che il valore di test eguagli 26: 26: // approssimativamente quello dell'oracolo // approssimativamente quello dell'oracolo
27: if (Numeric.approxEqual(y, oracleValue)) 27: if (Numeric.approxEqual(y, oracleValue)) 28: {
28: {
29: System.out.print("Test passed: ");
29: System.out.print("Test passed: ");
30: passcount++;
30: passcount++;
31: } 31: } 32: else 32: else 33: { 33: {
34: System.out.print("Test failed: ");
34: System.out.print("Test failed: ");
35: failcount++;
35: failcount++;
36: }
36: }
38 38
RootApproximatorHarness6 RootApproximatorHarness6
.java .java
37: System.out.println("square root = " + y 37: System.out.println("square root = " + y 38: + ", oracle = " + oracleValue);
38: + ", oracle = " + oracleValue);
39: } 39: }
40: System.out.println("Pass: " + passcount);
40: System.out.println("Pass: " + passcount);
41: System.out.println("Fail: " + failcount);
41: System.out.println("Fail: " + failcount);
42: }
42: }
43: }
43: }
39 39
RootApproximatorHarness6 RootApproximatorHarness6
.java .java
Output Output
Test passed: square root = 718.3849112194539, oracle = 718.3849112194538 Test passed: square root = 718.3849112194539, oracle = 718.3849112194538 Test passed: square root = 641.2739466673618, oracle = 641.2739466673619 Test passed: square root = 641.2739466673618, oracle = 641.2739466673619 Test passed: square root = 896.3559528159169, oracle = 896.3559528159169 Test passed: square root = 896.3559528159169, oracle = 896.3559528159169 Test passed: square root = 591.4264541724909, oracle = 591.4264541724909 Test passed: square root = 591.4264541724909, oracle = 591.4264541724909 Test passed: square root = 721.029957736384, oracle = 721.029957736384 Test passed: square root = 721.029957736384, oracle = 721.029957736384 . . .
. . .
Pass: 100 Pass: 100 Fail: 0 Fail: 0
40 40
Regression Testing Regression Testing
Conservare i casi di test Conservare i casi di test
Usare i test memorizzati in versioni successive Usare i test memorizzati in versioni successive
Una suite di test è un insieme di test per il collaudo Una suite di test è un insieme di test per il collaudo ripetuto
ripetuto
Ciclico = bug che è corretto ma riappare in Ciclico = bug che è corretto ma riappare in successive versioni
successive versioni
Regression testing: ripetizione di test precedenti per Regression testing: ripetizione di test precedenti per assicurare che punti deboli noti in versioni precedenti assicurare che punti deboli noti in versioni precedenti
non appaiano in nuove versioni
non appaiano in nuove versioni
41 41
Copertura del Test Copertura del Test
Test a scatola nera Test a scatola nera : funzionalità di collaudo che non : funzionalità di collaudo che non prende in considerazione la struttura interna
prende in considerazione la struttura interna dell'implementazione.
dell'implementazione.
Test a scatola bianca Test a scatola bianca : prendere in considerazione : prendere in considerazione la struttura intern nel progettare i test
la struttura intern nel progettare i test
Copertura Copertura : misura di quante parti di un programma : misura di quante parti di un programma siano state collaudate
siano state collaudate
Assicurarsi che ogni parte del programma sia Assicurarsi che ogni parte del programma sia interessata al meno una volta da un caso di test interessata al meno una volta da un caso di test
es. assicurarsi di eseguire ogni diramazione in almeno un es. assicurarsi di eseguire ogni diramazione in almeno un
42 42
Copertura del Test Copertura del Test
Suggerimento Suggerimento : scrivere casi di test prima di finire di : scrivere casi di test prima di finire di scrivere il programma
scrivere il programma
→ dà l'idea di quello che dovrebbe fare
→ dà l'idea di quello che dovrebbe fare
I programmi moderni possono essere ardui da I programmi moderni possono essere ardui da testare
testare
Interfacce grafiche (uso del mouse) Interfacce grafiche (uso del mouse)
Connessioni di rete (ritardi e cadute) Connessioni di rete (ritardi e cadute)
Ci sono strumenti pr l'automazione del test in tali situazioni Ci sono strumenti pr l'automazione del test in tali situazioni
Principi di base del regression testing della copertura Principi di base del regression testing della copertura completa sono ancora validi
completa sono ancora validi
43 43
Colludare Unità con JUnit Colludare Unità con JUnit
http://junit.org http://junit.org
Inglobato in IDE come BlueJ e Eclipse Inglobato in IDE come BlueJ e Eclipse
Filosofia Filosofia : ogni volta che si implementa una classe si : ogni volta che si implementa una classe si implementi anche una classe di test
implementi anche una classe di test
44 44
Colludare Unità con JUnit
Colludare Unità con JUnit
45 45
Trace di un Programma Trace di un Programma
Messaggi che mostrano il flusso dell'esecuzione Messaggi che mostrano il flusso dell'esecuzione
if (status == SINGLE) if (status == SINGLE) { {
System.out.println("status is SINGLE");
System.out.println("status is SINGLE");
. . . . . . } }
. . .
. . .
46 46
Trace di un Programma Trace di un Programma
Svantaggio Svantaggio : serve rimuoverli a collaudo terminato, : serve rimuoverli a collaudo terminato, rimettere quando si trova un altro errore
rimettere quando si trova un altro errore
Soluzione Soluzione : usare la classe : usare la classe Logger Logger disabilitare i disabilitare i
messaggi di trace senza rimuoverli dal programma
messaggi di trace senza rimuoverli dal programma
47 47
Logging Logging
I messaggi di log possono essere disattivati quando I messaggi di log possono essere disattivati quando il collaudo è completato
il collaudo è completato
Si usa l'ggetto globale Si usa l'ggetto globale Logger.global Logger.global
Registrare (log) un messaggio: Registrare (log) un messaggio:
Logger.global.info("status is SINGLE");
Logger.global.info("status is SINGLE");
48 48
Logging Logging
Per default, i messaggi registrati sono stampati. Per default, i messaggi registrati sono stampati.
Li si disabilita con:
Li si disabilita con:
Il logging può diventare assillante Il logging può diventare assillante (meglio nè troppo nè troppo poco) (meglio nè troppo nè troppo poco)
Alcuni programmatori al logging preferiscono il Alcuni programmatori al logging preferiscono il debugging (prossima sezione)
debugging (prossima sezione)
Logger.global.setLevel(Level.OFF);
Logger.global.setLevel(Level.OFF);
49 49
Logging Logging
Quando si traccia il flusso d'esecuzione, gli eventi Quando si traccia il flusso d'esecuzione, gli eventi più importanti sono l'entrata e l'uscita dai metodi più importanti sono l'entrata e l'uscita dai metodi
All'inizio di un metodo, stampa dei parametri: All'inizio di un metodo, stampa dei parametri:
public TaxReturn(double anIncome, int aStatus) public TaxReturn(double anIncome, int aStatus) { {
Logger.global.info("Parameters: anIncome = " + anIncome Logger.global.info("Parameters: anIncome = " + anIncome + " aStatus = " + aStatus);
+ " aStatus = " + aStatus);
. . .
. . .
} }
50 50
Logging Logging
Alla fine di un metodo, si stampa il valore di ritorno: Alla fine di un metodo, si stampa il valore di ritorno:
public double getTax() public double getTax() { {
. . . . . .
Logger.global.info("Return value = " + tax);
Logger.global.info("Return value = " + tax);
return tax;
return tax;
} }
51 51
Usare un Debugger Usare un Debugger
Debugger = applicazione che fa girare un Debugger = applicazione che fa girare un
programma e ne analizza il comportamento in fase programma e ne analizza il comportamento in fase
d'esecuzione d'esecuzione
Un debugger permette di fermare e far ripartire il Un debugger permette di fermare e far ripartire il proprio programma, osservare il contenuto delle proprio programma, osservare il contenuto delle
variabili ed eseguirlo passo passo variabili ed eseguirlo passo passo
Più grandi sono i programmi, più sono difficili da Più grandi sono i programmi, più sono difficili da debuggare semplicemente mediante logging
debuggare semplicemente mediante logging
52 52
Usare un Debugger Usare un Debugger
I debugger possono essere una parte della propria I debugger possono essere una parte della propria IDE (Eclipse, BlueJ) o programmi separati (JSwat) IDE (Eclipse, BlueJ) o programmi separati (JSwat)
Tre concetti-chiave: Tre concetti-chiave:
Breakpoint Breakpoint
Esecuzione istruzione per istruzione Esecuzione istruzione per istruzione
Osservazione delle variabili Osservazione delle variabili
53 53
Debugger Fermo Debugger Fermo ad un Breakpoint ad un Breakpoint
Figure 3:
54 54
Controllare le Variabili Controllare le Variabili
Figure 4:
55 55
Debugging Debugging
L'esecuzione è sospesa ogni volta che si raggiunge L'esecuzione è sospesa ogni volta che si raggiunge breakpoint
breakpoint
In un debugger, un programma gira a piena velocità In un debugger, un programma gira a piena velocità fino a quando non si raggiunge un breakpoint
fino a quando non si raggiunge un breakpoint
Quando l'esecuzione si interrompe, si può: Quando l'esecuzione si interrompe, si può:
Controllare il contenuto delle variabili Controllare il contenuto delle variabili
Far eseguire il programma una riga alla volta Far eseguire il programma una riga alla volta
oppure, continuare a farlo girare a piena velocità fino al oppure, continuare a farlo girare a piena velocità fino al prossimo breakpoint
prossimo breakpoint
56 56
Debugging Debugging
Quando il programma termina, anche il debugger si Quando il programma termina, anche il debugger si ferma
ferma
I breakpoint rimangono attivi fino alla loro rimozione I breakpoint rimangono attivi fino alla loro rimozione
Due comandi per l'esecuzione di un singolo passo: Due comandi per l'esecuzione di un singolo passo:
Step Over: salta le chiamate dei metodi Step Over: salta le chiamate dei metodi
Step Into: entra nei metodi chiamati Step Into: entra nei metodi chiamati
57 57
Single-Step Example Single-Step Example
Riga corrente: Riga corrente:
Quando si saltano le chiamate di metodi, si passa Quando si saltano le chiamate di metodi, si passa alla riga successiva:
alla riga successiva:
String input = in.next();
String input = in.next();
Word w = new Word(input);
Word w = new Word(input);
int syllables = w.countSyllables();
int syllables = w.countSyllables();
System.out.println("Syllables in " + input + ": " + syllables);
System.out.println("Syllables in " + input + ": " + syllables);
String input = in.next();
String input = in.next();
Word w = new Word(input);
Word w = new Word(input);
int syllables = w.countSyllables();
int syllables = w.countSyllables();
System.out.println("Syllables in " + input + ": " + syllables);
System.out.println("Syllables in " + input + ": " + syllables);
58 58
Esempio di Single-Step Esempio di Single-Step
Invece, entrando nelle chiamate, si passa alla prima Invece, entrando nelle chiamate, si passa alla prima riga di codice del metodo
riga di codice del metodo countSyllables countSyllables : :
public int countSyllables() public int countSyllables() { {
int count = 0; int count = 0;
int end = text.length() - 1;
int end = text.length() - 1;
. . .
. . .
} }
59 59
Sessione d'Esempio Sessione d'Esempio
di Debugging di Debugging
Word Word : classe oper contare le silabe i una parola : classe oper contare le silabe i una parola
Ogni grupo di vocali adiacenti (a, e, i, o, u, y) conta Ogni grupo di vocali adiacenti (a, e, i, o, u, y) conta come una sillaba
come una sillaba
Però, una e a fine parola non conta come sillaba Però, una e a fine parola non conta come sillaba
Se l'algoritmo restituisce 0, si incrementi ad 1 Se l'algoritmo restituisce 0, si incrementi ad 1
Il costruttore rimuove i caratteri non-lettera all'inizio Il costruttore rimuove i caratteri non-lettera all'inizio ed alla fine
ed alla fine
60 60
Word.java Word.java
01: /**
01: /**
02: Questa classe descrive una parola in un documento.
02: Questa classe descrive una parola in un documento.
03: */
03: */
04: public class Word 04: public class Word 05: {
05: {
06: /**
06: /**
07: Construisce una parola togliendo i caratteri non 07: Construisce una parola togliendo i caratteri non 08: alfabetici ad inizio e fine, come la punteggiatura 08: alfabetici ad inizio e fine, come la punteggiatura 09: @param s la stringa in input
09: @param s la stringa in input 10: */
10: */
11: public Word(String s) 11: public Word(String s) 12: {
12: {
13: int i = 0;
13: int i = 0;
14: while (i < s.length() &&
14: while (i < s.length() &&
!Character.isLetter(s.charAt(i))) !Character.isLetter(s.charAt(i))) 15: i++;
15: i++;
16: int j = s.length() - 1;
16: int j = s.length() - 1;
17: while (j > i && !Character.isLetter(s.charAt(j))) 17: while (j > i && !Character.isLetter(s.charAt(j))) 18: j--;
18: j--;
19: text = s.substring(i, j);
19: text = s.substring(i, j);
20: }
20: }
61 61
Word.java Word.java
21: 21:
22: /**
22: /**
23: Restituisce il testo della parola rimossi 23: Restituisce il testo della parola rimossi 24: i caratteri non alfabetici iniziali e finali.
24: i caratteri non alfabetici iniziali e finali.
25: @return il testo della parola 25: @return il testo della parola 26: */
26: */
27: public String getText() 27: public String getText() 28: {
28: {
29: return text;
29: return text;
30: } 30: } 31: 31:
32: /**
32: /**
33: Counta le sillabe nella parola.
33: Counta le sillabe nella parola.
34: @return il conto delle sillabe 34: @return il conto delle sillabe 35: */
35: */
62 62
Word.java Word.java
36: public int countSyllables() 36: public int countSyllables() 37: {
37: {
38: int count = 0;
38: int count = 0;
39: int end = text.length() - 1;
39: int end = text.length() - 1;
40: if (end < 0) return 0;
40: if (end < 0) return 0;
// la stringa vuota non ha sillabe // la stringa vuota non ha sillabe 41: 41:
42: // Una e alla fine della parola non conta come vocale 42: // Una e alla fine della parola non conta come vocale 43: char ch = Character.toLowerCase(text.charAt(end));
43: char ch = Character.toLowerCase(text.charAt(end));
44: if (ch == 'e') end--;
44: if (ch == 'e') end--;
45: 45:
46: boolean insideVowelGroup = false;
46: boolean insideVowelGroup = false;
47: for (int i = 0; i <= end; i++) 47: for (int i = 0; i <= end; i++) 48: {
48: {
49: ch = Character.toLowerCase(text.charAt(i));
49: ch = Character.toLowerCase(text.charAt(i));
50: String vowels = "aeiouy";
50: String vowels = "aeiouy";
51: if (vowels.indexOf(ch) >= 0) 51: if (vowels.indexOf(ch) >= 0) 52: {
52: {
63 63
Word.java Word.java
53: // ch is a vowel 53: // ch is a vowel
54: if (!insideVowelGroup) 54: if (!insideVowelGroup) 55: {
55: {
56: // Inizio di un nuovo gruppo di vocali 56: // Inizio di un nuovo gruppo di vocali 57: count++;
57: count++;
58: insideVowelGroup = true;
58: insideVowelGroup = true;
59: } 59: } 60: } 60: } 61: } 61: } 62: 62:
63: // ogni parola ha minimo una sillaba 63: // ogni parola ha minimo una sillaba 64: if (count == 0)
64: if (count == 0) 65: count = 1;
65: count = 1;
66: 66:
67: return count;
67: return count;
68: } 68: } 69: 69:
70: private String text;
70: private String text;
71: }
71: }
64 64
WordTester.java WordTester.java
01: import java.util.Scanner;
01: import java.util.Scanner;
02: 02:
03: /**
03: /**
04: Questo programma testa il metodo countSyllables della 04: Questo programma testa il metodo countSyllables della classe Word
classe Word 05: */
05: */
06: public class WordTester 06: public class WordTester 07: {
07: {
08: public static void main(String[] args) 08: public static void main(String[] args) 09: {
09: {
10: Scanner in = new Scanner(System.in);
10: Scanner in = new Scanner(System.in);
11: 11:
12: System.out.println("Enter a sentence ending in a 12: System.out.println("Enter a sentence ending in a
// period."); // period.");
13: 13:
14: String input;
14: String input;
15: do
15: do
16: {
16: {
65 65
WordTester.java WordTester.java
17: input = in.next();
17: input = in.next();
18: Word w = new Word(input);
18: Word w = new Word(input);
19: int syllables = w.countSyllables();
19: int syllables = w.countSyllables();
20: System.out.println("Syllables in " + input + ": "
20: System.out.println("Syllables in " + input + ": "
21: + syllables);
21: + syllables);
22: } 22: }
23: while (!input.endsWith("."));
23: while (!input.endsWith("."));
24: }
24: }
25: }
25: }
66 66
Debuggare il Programma Debuggare il Programma
output non corretto ( output non corretto ( buggy buggy ) ) (per l'input
(per l'input "hello yellow peach" "hello yellow peach" ): ):
Porre un breakpoint sulla prima riga di Porre un breakpoint sulla prima riga di countSyllables
countSyllables della classe della classe Word Word
Far partire il programma, fornire l'input. Far partire il programma, fornire l'input.
Il programma si ferma al breakpoint Il programma si ferma al breakpoint
Il metodo controlla se la lettera finale è una ' Il metodo controlla se la lettera finale è una ' e e ' '
Syllables in hello: 1
Syllables in hello: 1
Syllables in yellow: 1
Syllables in yellow: 1
Syllables in peach: 1
Syllables in peach: 1
67 67
Debuggare il Programma Debuggare il Programma
Figure 5:
Debugging the CountSyllables Method
68 68
Debuggare il Programma Debuggare il Programma
Controllare se funziona: Controllare se funziona:
eseguire fino alla riga dove si fa il controllo ed eseguire fino alla riga dove si fa il controllo ed
osservare la variabile
osservare la variabile ch ch
Dovrebbe contenere la lettera finale ma contiene una Dovrebbe contenere la lettera finale ma contiene una
' ' l l ' '
69 69
Trovare Altri Problemi Trovare Altri Problemi
Figure 6:
70 70
Trovare Altri Problemi Trovare Altri Problemi
end end viene posta a viene posta a 3 3 , non a , non a 4 4
text text contiene " contiene " hell hell ", non " ", non " hello hello " "
No c'è da sorprendersi se No c'è da sorprendersi se countSyllables countSyllables restituisce
restituisce 1 1
Il colpevole è altrove Il colpevole è altrove
Non riesce a tornare in tempo Non riesce a tornare in tempo
Far partire di nuovo ponendo un breakpoint nel Far partire di nuovo ponendo un breakpoint nel costruttore di
costruttore di Word Word
71 71
Debuggare Debuggare il Costruttore di
il Costruttore di Word Word
Fornire l'input " Fornire l'input " hello hello " di nuovo " di nuovo
Fermare subito dopo la fine del secondo loop nel Fermare subito dopo la fine del secondo loop nel costruttore
costruttore
Controllare le variabili Controllare le variabili i i e e j j
I valori sono I valori sono 0 0 e e 4 4
giusto dato che l'input consiste di lettere giusto dato che l'input consiste di lettere
Perchè il testo viene impostato ad " Perchè il testo viene impostato ad " hell hell "? "?
72 72
Debuggare Debuggare il Costruttore di Word il Costruttore di Word
Errore di fuori-di-uno: il secondo parametro della Errore di fuori-di-uno: il secondo parametro della sotto-stringa è la prima posizione da non includere sotto-stringa è la prima posizione da non includere
text = substring(i, j); text = substring(i, j);
dovrebbe essere dovrebbe essere
text = substring(i, j+1);
text = substring(i, j+1);
73 73
Debuggare Debuggare il Costruttore di Word il Costruttore di Word
Figure 7:
Debugging the Word