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