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