Upload
lucinda-gallagher
View
219
Download
3
Embed Size (px)
Citation preview
Chapter 10Chapter 10
Testing and Debugging Testing and Debugging
Chapter GoalsChapter Goals
►To learn how To learn how to carry out unit teststo carry out unit tests►To understand the To understand the principles of principles of
test case selection and evaluation test case selection and evaluation ►To learn how To learn how to use logging to use logging ►To become To become familiar with the familiar with the
debuggerdebugger ►To learn To learn strategies for effective strategies for effective
debuggingdebugging
The First BugThe First Bug
Unit TestUnit Test
Test classes in isolation, outside Test classes in isolation, outside the program in which they are the program in which they are used used
Test one class at a timeTest one class at a time Supply test inputs through Supply test inputs through test test
harnessharness Test harness = program that Test harness = program that
feeds test inputs to a class feeds test inputs to a class
Root ApproximatorRoot Approximator
► Example program to illustrate testingExample program to illustrate testing: : square root approximator Algorithm square root approximator Algorithm
known to the ancient Greeks (Heron) known to the ancient Greeks (Heron) ► Task:Task: to compute the square root of to compute the square root of aa ► Given:Given: a guess a guess x x (we start with (we start with x=ax=a) )
The actual square root lies between The actual square root lies between a/x a/x and and xx
► Take midpoint (Take midpoint (x x + + aa//xx) / 2 as a better guess ) / 2 as a better guess Method converges rapidly. Method converges rapidly.
► Square root of 100:Square root of 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.0Guess #8: 10.0All the other guesses from this point on, return the All the other guesses from this point on, return the same valuesame value
File RootApproximator.javaFile RootApproximator.java11 /** /** 22 Computes approximations to the square root Computes approximations to the square root 33 of a number, using Heron's algorithm of a number, using Heron's algorithm 44 */ */ 55 public class RootApproximator public class RootApproximator 66 { {77 /** /** 88 Constructs a root approximator for a given number Constructs a root approximator for a given number 99 @param aNumber the number from which to extract the square root @param aNumber the number from which to extract the square root 1010 (Precondition: aNumber >= 0) (Precondition: aNumber >= 0) 1111 */ */ 1212 public RootApproximator(double aNumber) public RootApproximator(double aNumber) 1313 { { a = aNumber; a = aNumber; 15 15 xold = 1; xold = 1; 1616 xnew = a; xnew = a; 1717 } }
1818
1919 /** /**
2020 Compute a better guess from the current guess. Compute a better guess from the current guess.
2121 @return the next guess @return the next guess
2222 */ */
2323 public double nextGuess() public double nextGuess()
2424 { { xold = xnew; xold = xnew;
2626 if (xold != 0) if (xold != 0)
2727 xnew = (xold + a / xold) / 2; xnew = (xold + a / xold) / 2;
2828 return xnew; return xnew;
2929 } }
3030
3131 /** /**
3232 Compute the root by repeatedly improving Compute the root by repeatedly improving
3333 the current guess until two successive guesses are the current guess until two successive guesses are approximately equal. approximately equal.
3434 @return the computed value for the square root @return the computed value for the square root 3535 */ */ 3636 public double getRoot() public double getRoot() 3737 { { 3838 while (!Numeric.approxEqual(xnew, xold)) while (!Numeric.approxEqual(xnew, xold)) 3939 nextGuess(); nextGuess(); 4040 return xnew; return xnew; 4141 } } 42424343 private double a; // the number whose square root is private double a; // the number whose square root is
computed computed 4444 private double xnew; // the current guess private double xnew; // the current guess 4545 private double xold; // the old guess private double xold; // the old guess 4646 } }
File File RootApproximatorTest.javaRootApproximatorTest.java
11 import javax.swing.JOptionPane; import javax.swing.JOptionPane; 22 /** /** This program prints ten approximations for a square rootThis program prints ten approximations for a square root.. 33 */ */ 44 public class RootApproximatorTest public class RootApproximatorTest 55 { { 66 public static void main(String[] args) public static void main(String[] args) 77 { { 88 String input = JOptionPane.showInputDialog("Enter a number"); String input = JOptionPane.showInputDialog("Enter a number"); 99 double x = Double.parseDouble(input); double x = Double.parseDouble(input); 1010 RootApproximator r = new RootApproximator(x); RootApproximator r = new RootApproximator(x); 1111 final int MAX_TRIES = 10; final int MAX_TRIES = 10; 1212 for (int tries = 1; tries <= MAX_TRIES; tries++) for (int tries = 1; tries <= MAX_TRIES; tries++) 1313 { double y = r.nextGuess(); { double y = r.nextGuess(); 1414 System.out.println("Guess #" + tries + ": " + y); System.out.println("Guess #" + tries + ": " + y); 1515 } } 1616 System.exit(0); System.exit(0); 1717 } }1818 } }
File RootApproximatorTest2File RootApproximatorTest211 import javax.swing.JOptionPane; import javax.swing.JOptionPane; 22 /** /** This program computes square roots of user-supplied inputs. This program computes square roots of user-supplied inputs. 33 */ */ 44 public class RootApproximatorTest2 public class RootApproximatorTest2 55 { public static void main(String[] args) { public static void main(String[] args) 66 { { 77 boolean done = false; boolean done = false; 88 while (!done) while (!done) 99 { String input = JOptionPane.showInputDialog("Enter a number, C to quit"); { String input = JOptionPane.showInputDialog("Enter a number, C to quit"); 1010 if (input == null) if (input == null) 1212 done = true; done = true; 1313 else else 1414 { { double x = Double.parseDouble(input); double x = Double.parseDouble(input); 1616 RootApproximator r = new RootApproximator(x); RootApproximator r = new RootApproximator(x); 17 17 double y = r.getRoot(); double y = r.getRoot(); 1818 System.out.println("square root of " + x + " = " + y); System.out.println("square root of " + x + " = " + y); 2020 } } 2121 } } 2222 System.exit(0); System.exit(0); 2323 } } 2424 } }
Sources of Test DataSources of Test Data
►Provided by humansProvided by humansRootApproximatorTest2RootApproximatorTest2
►Computer-generated sequenceComputer-generated sequenceRootApproximatorTest3 RootApproximatorTest3
►Random sequenceRandom sequenceRootApproximatorTest4 RootApproximatorTest4
File File RootApproximatorTest3.javaRootApproximatorTest3.java11 /** /** This program computes square roots of input values This program computes square roots of input values
22 computer generated by a loop. computer generated by a loop. */ */
33 public class RootApproximatorTest3 public class RootApproximatorTest3
44 { public static void main(String[] args) { public static void main(String[] args)
55 { {
66 final double MIN = 1;final double MIN = 1;
77 final double MAX = 10; final double MAX = 10;
88 final double INCREMENT = 0.5; final double INCREMENT = 0.5;
99 for (double x = MIN; x <= MAX; x = x + INCREMENT) for (double x = MIN; x <= MAX; x = x + INCREMENT)
1010 { {
11 11 RootApproximator r = new RootApproximator(x); RootApproximator r = new RootApproximator(x);
1212 double y = r.getRoot(); double y = r.getRoot();
1313 System.out.println("square root of " + x + " = " + y); System.out.println("square root of " + x + " = " + y);
1414 } } } } } }
File File RootApproximatorTest4.javaRootApproximatorTest4.java11 import java.util.Random; import java.util.Random;
22 /** /** This program computes square roots of random inputsThis program computes square roots of random inputs. . */ */
33 public class RootApproximatorTest4 public class RootApproximatorTest4
44 { public static void main(String[] args) { public static void main(String[] args)
55 { { final double SAMPLES = 100; final double SAMPLES = 100;
66 Random generator = new Random(); Random generator = new Random();
77 for (int i = 1; i <= SAMPLES; i++) for (int i = 1; i <= SAMPLES; i++)
88 { // generate random test value { // generate random test value
99 double x = 1.0E6 * generator.nextDouble(); double x = 1.0E6 * generator.nextDouble();
1010 RootApproximator r = new RootApproximator(x); RootApproximator r = new RootApproximator(x);
1111 double y = r.getRoot(); double y = r.getRoot();
1212 System.out.println("square root of " + x + " = " + y); System.out.println("square root of " + x + " = " + y);
1313 } }
1414 } }
1515 } }
Test CasesTest Cases Positive test case:Positive test case: expect positive expect positive
outcomeoutcomeE.g. square root of 100 E.g. square root of 100
Negative test case:Negative test case: expect negative expect negative outcomeoutcomeE.g. square root of 100 E.g. square root of 100
Boundary test case:Boundary test case: at boundary of at boundary of domaindomainFrequent cause for errorsFrequent cause for errorsE.g. square root of 0E.g. square root of 0
Test Case EvaluationTest Case Evaluation ManualManual
E.g. You know the result and you verify if the E.g. You know the result and you verify if the program has produced the correct resultprogram has produced the correct result
Check property of resultCheck property of resultE.g. square root squared = original valueE.g. square root squared = original value
Oracle = slower way of computing answerOracle = slower way of computing answerE.g. square root of E.g. square root of x = xx = x1/21/2
Regression TestingRegression Testing
► Save test cases Automate testingSave test cases Automate testing► java Program < test1.in > test1.outjava Program < test1.in > test1.out
java Program < test2.in > test2.outjava Program < test2.in > test2.outjava Program < test3.in > test3.outjava Program < test3.in > test3.out
► Repeat test whenever program changes Repeat test whenever program changes Test suite = collection of test cases Test suite = collection of test cases Cycling = bug that is fixed but Cycling = bug that is fixed but
reappears in later versionreappears in later version Regression testing = testing against Regression testing = testing against
past failurespast failures
Test CoverageTest Coverage
► Black-box testing:Black-box testing: test functionality test functionality without understanding internal structure without understanding internal structure
► White-box testing:White-box testing: take internal take internal structure into account when designing structure into account when designing tests tests
► Test coverage:Test coverage: the code that is actually the code that is actually executed during test cases executed during test cases
► Easy to overlook errorEasy to overlook error branches during branches during testingtesting Make sure to execute each branch in at Make sure to execute each branch in at
least one test case least one test case
Program TraceProgram Trace Output statements in your program for Output statements in your program for
diagnostic purposesdiagnostic purposes
if (status == SINGLE) if (status == SINGLE) { { System.out.println("status is SINGLE"); System.out.println("status is SINGLE"); ... ... } } ......
LoggingLogging
1.1. Get global logger object:Get global logger object:Logger logger = Logger.getLogger("global");Logger logger = Logger.getLogger("global");
2.2. Log a messageLog a messagelogger.info("status is SINGLE");logger.info("status is SINGLE");
3.3. By default, logged messages are printed. By default, logged messages are printed. Turn them off withTurn them off withlogger.setLevel(Level.OFF); logger.setLevel(Level.OFF);
Remember to import java.util.logging.Logger Remember to import java.util.logging.Logger
The DebuggerThe Debugger DebuggerDebugger = program to run your = program to run your
program, interrupt it, and inspect program, interrupt it, and inspect variables variables
Three key commands: Three key commands: Set Breakpoint Set Breakpoint Single Step Single Step Inspect Variable Inspect Variable
Two variations of Single Step Two variations of Single Step Step Over = don't enter method call Step Over = don't enter method call Step Inside = enter method callStep Inside = enter method call
The Debugger Stopping at a The Debugger Stopping at a BreakpointBreakpoint
Inspecting VariablesInspecting Variables
Sample Debugging SessionSample Debugging Session Word class counts syllables in a word Word class counts syllables in a word Each group of adjacent vowels (aeiouy) is a Each group of adjacent vowels (aeiouy) is a
syllable syllable However, an e at the end of a word doesn't However, an e at the end of a word doesn't
count count If algorithm gives count of 0, increment to 1 If algorithm gives count of 0, increment to 1 Constructor removes non-letters at Constructor removes non-letters at
beginning and end beginning and end Buggy output:Buggy output: Syllables in hello: 1Syllables in hello: 1
Syllables in regal: 1Syllables in regal: 1Syllables in real: 1Syllables in real: 1
File Word.javaFile Word.java11 public class Word public class Word
22 { {
33 /** /** Constructs a word by removing leading and trailingConstructs a word by removing leading and trailing
44 non-letter characters, such as punctuation marks. non-letter characters, such as punctuation marks.
55 @param s the input string @param s the input string */ */
66 public Word(String s) public Word(String s)
77 { int i = 0; { int i = 0;
88 while (i < s.length() && !Character.isLetter(s.charAt(i))) while (i < s.length() && !Character.isLetter(s.charAt(i)))
99 i++; i++;
1010 int j = s.length() - 1; int j = s.length() - 1;
1111 while (j > i && !Character.isLetter(s.charAt(j))) while (j > i && !Character.isLetter(s.charAt(j)))
1212 j--; j--;
1313 text = s.substring(i, j + 1); text = s.substring(i, j + 1);
1414 } }
1515
1616 /** /** Returns the text of the word, after removal ofReturns the text of the word, after removal of
1717 the leading and trailing non-letter characters. the leading and trailing non-letter characters.
1818 @return the text of the word @return the text of the word */ */
1919 public String getText() public String getText()
2020 { return text; } { return text; }
2121
2222 /** /** Counts the syllables in the word. Counts the syllables in the word.
2323 @return the syllable count @return the syllable count */ */
2424 public int countSyllables() public int countSyllables()
2525 { int count = 0; { int count = 0;
2626 int end = text.length() - 1; int end = text.length() - 1;
2727 if (end < 0) return 0; if (end < 0) return 0; // the empty string has no // the empty string has no syllablessyllables
2828
2929 // An e at the end of the word doesn't count as a vowel // An e at the end of the word doesn't count as a vowel
3030 char ch = Character.toLowerCase(text.charAt(end)); char ch = Character.toLowerCase(text.charAt(end));
3131 if (ch == 'e') end--; if (ch == 'e') end--;
3232 boolean insideVowelGroup = false; boolean insideVowelGroup = false;
3333 for (int i = 0; i <= end; i++) for (int i = 0; i <= end; i++)
3434 { { ch = Character.toLowerCase(text.charAt(i)); ch = Character.toLowerCase(text.charAt(i));
3535 if ("aeiouy".indexOf(ch) >= 0) if ("aeiouy".indexOf(ch) >= 0)
3636 { // ch is a vowel { // ch is a vowel
3737 if (!insideVowelGroup) if (!insideVowelGroup)
3838 { // start of new vowel group { // start of new vowel group
3939 count++; count++;
4040 insideVowelGroup = true; insideVowelGroup = true;
4141 } }
4242 } }
4343 4444 // Every word has at least one syllable // Every word has at least one syllable 4545 if (count == 0) if (count == 0) 4646 count = 1; count = 1; 4747 return count; return count; 4848 } } 4949 private String text; private String text; 5050 } }
File WordTest.javaFile WordTest.java11 import java.util.StringTokenizer; import java.util.StringTokenizer;
22 import javax.swing.JOptionPane; import javax.swing.JOptionPane;
33 public class WordTest public class WordTest
44 { public static void main(String[] args) { public static void main(String[] args)
55 { {
66 String input = JOptionPane.showInputDialog("Enter a sentence"); String input = JOptionPane.showInputDialog("Enter a sentence");
77 StringTokenizer tokenizer = new StringTokenizer(input); StringTokenizer tokenizer = new StringTokenizer(input);
88 while (tokenizer.hasMoreTokens()) while (tokenizer.hasMoreTokens())
99 { {
1010 String token = tokenizer.nextToken(); String token = tokenizer.nextToken();
1111 Word w = new Word(token); Word w = new Word(token);
1212 int syllables = w.countSyllables(); int syllables = w.countSyllables();
1313 System.out.println("Syllables in " + w.getText() + ": " + System.out.println("Syllables in " + w.getText() + ": " + syllables); syllables);
1414 } }
1515 System.exit(0); System.exit(0);
1616 } } } }
Final Letter Test is Not Final Letter Test is Not CorrectCorrect
Set breakpoint in line 35 (first line of Set breakpoint in line 35 (first line of countSyllables) countSyllables)
Start program, supply input hello Start program, supply input hello Method checks if final letter is 'e'Method checks if final letter is 'e' Run to line 41 Run to line 41 Inspect variable ch Inspect variable ch Should contain final letter but contains Should contain final letter but contains
'l''l'
Debugging the countSyllables Debugging the countSyllables MethodMethod
The Current Values of the Local The Current Values of the Local and Instance Variablesand Instance Variables
More Problems FoundMore Problems Found
end is set to 3, not 4 end is set to 3, not 4 text contains "hell", not "hello" text contains "hell", not "hello" No wonder countSyllables returns 1 No wonder countSyllables returns 1 Culprit is elsewhere Culprit is elsewhere Can't go back in time Can't go back in time Restart and set breakpoint in Word Restart and set breakpoint in Word
constructor constructor
Debugging the Word Debugging the Word ConstructorConstructor
Supply "hello" input again Supply "hello" input again Break past the end of second loop in constructor Break past the end of second loop in constructor Inspect i and j Inspect i and j They are 0 and 4--makes sense since the input They are 0 and 4--makes sense since the input
consists of letters consists of letters Why is text set to "hell"? Why is text set to "hell"? Off-by-one error: Second parameter of substring Off-by-one error: Second parameter of substring
is the first position is the first position notnot to include to include text = substring(i, j);text = substring(i, j);
should beshould betext = substring(i, j + 1);text = substring(i, j + 1);
Debugging the Word Debugging the Word ConstructorConstructor
Another ErrorAnother Error Fix the error Fix the error Recompile Recompile Test again:Test again: Syllables in hello: 1Syllables in hello: 1
Syllables in regal: 1Syllables in regal: 1Syllables in real: 1Syllables in real: 1
Oh no, it's still not right Oh no, it's still not right Start debuggerStart debugger Erase all old breakpoints Erase all old breakpoints Supply input "regal"Supply input "regal"
Debugging countSyllables Debugging countSyllables (again)(again)
Break in the beginning of countSyllables Break in the beginning of countSyllables Single-step through loop Single-step through loop First iteration ('r'): skips test for vowel First iteration ('r'): skips test for vowel Second iteration ('e'): passes test, increments count Second iteration ('e'): passes test, increments count Third iteration ('g'): skips test Third iteration ('g'): skips test Fourth iteration ('a'): passes test, but doesn't increment Fourth iteration ('a'): passes test, but doesn't increment
countcount insideVowelGroup was never reset to false insideVowelGroup was never reset to false Fix and retest: All test cases pass Fix and retest: All test cases pass Is the program now bug-free? The debugger can't answer Is the program now bug-free? The debugger can't answer
that. that.