0% found this document useful (0 votes)
18 views68 pages

CH 03

This document discusses testing strategies and the JUnit testing framework. It covers different types of testing like unit testing, integration testing, and black-box vs white-box testing. It emphasizes the importance of testing all paths through code. It also introduces concepts like stubs, drivers, preconditions and postconditions. Finally, it provides an example of how to write JUnit tests to test a method that searches an array, testing different cases like when the target is found, not found, or there are multiple matches.

Uploaded by

ajax12developer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views68 pages

CH 03

This document discusses testing strategies and the JUnit testing framework. It covers different types of testing like unit testing, integration testing, and black-box vs white-box testing. It emphasizes the importance of testing all paths through code. It also introduces concepts like stubs, drivers, preconditions and postconditions. Finally, it provides an example of how to write JUnit tests to test a method that searches an array, testing different cases like when the target is found, not found, or there are multiple matches.

Uploaded by

ajax12developer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 68

CHAPTER 3

Testing and Debugging


Chapter Objectives
 To understand different testing strategies
 To learn to test using the Junit test framework
 To learn to use test driven development
 To learn to use a debugger within a Java IDE
Types of Testing
Section 3.1
Types of Testing
 Testing is exercising a program under
controlled conditions.
 More thorough testing increases the
likelihood of finding defects.
 However, in a complex program, no
amount of testing can guarantee the
absence of defects.
Levels of Testing
 Unit testing
 Tests the smallest testable pieces of the software
 In OOD, this may be a class or a method.
 Integration testing
 Tests interaction among units
 If the unit is a method, this tests the interaction of methods within a class
 More commonly, tests the interaction between several classes
 System testing
 Tests the whole program in the context in which it will be used
 Acceptance testing
 System testing designed to demonstrate that the program meets its
functional requirements
Types of Testing
6

 Black-box testing
 Tests the item based on its interfaces and functional
requirements
 Input values are varied over allowable ranges and
outputs compared to independently calculated values.
 Input values outside of allowed ranges are also tested
to see if the unit responds according to specifications
Types of Testing (cont.)

 White-box testing
 Tests the unit with knowledge of its internal structure
 Attempts to exercise as many paths through the unit as
possible
 Statement coverage ensures that each statement is executed
at least once
 Branch coverage ensures that every choice at each branch is
tested
 Path coverage tests each path through a method
Example – testing all paths
8

 We want to test all of the paths of the following


method.
public void testMethod(char a, char b) {
if (a < 'M') {
if (b < 'X') {
System.out.println("path 1");
...
} else {
System.out.println("path 2");
...
}
} else {
if (b < 'C') {
System.out.println("path 3");
...
} else {
System.out.println("path 4");
...
}
}
}
Example (cont.)
9

 The following table shows possible input values to


exercise all four possible paths:
a b Message

‘A’ ‘A’ path 1

‘A’ ‘Z’ path 2

‘Z’ ‘A’ path 3

‘Z’ ‘Z’ path 4

 These are the smallest and largest allowable values


 A more complete test should use additional valid
combinations of values and with non-letter values
Preparations for Testing
10

 Planning for testing should begin early and include


consideration of:
 How will the program be tested?
 When will it be tested?
 By whom will it be tested?
 What test data will be used?
 Early planning can help programmers prepare for
testing as they write their code.
 For instance, validating input data and throwing
appropriate exceptions.
Testing Tips
11

 Document all class attributes and method


parameters using comments.
 Trace execution by displaying each method name
as it is entered.
 Display values of all input parameters as a method
is entered. Also any class attributes used.
 After a method returns, display its return value and
the values of any class attributes it modified.
Testing Tips (cont.)
12

 It is useful to include code like


if (TESTING) {
//code that you wish to “remove”
}
 Then you can add the following to your class when
you want to enable testing
private static final boolean TESTING = true;
 And change it when you want to disable testing
private static final boolean TESTING = false;
Specifying the Tests
Section 3.2
Specifying the Tests – General
Principles
 Black-box testing
 Test all expected input values
 Test unexpected input values
 Specify anticipated results of each set of values tested
Specifying the Tests – General
15
Principles (cont.)
 White-box testing
 Exercise every branch of every if statement
 Test switch statements for all valid selector values and
some invalid values
 Loops – test behavior if
 The body is never executed
 The body is executed once
 The body is executed the maximum number of times
 Assure that loops eventually will terminate
Boundary Conditions
16

 Boundary conditions are special cases which


should be explicitly tested.
 For instance, in a method designed to find a
specific value within an array, you would test cases
where
 The target is the first element in the array
 The target is the last element in the array
 The target is somewhere in the middle of the array
 The target is not in the array
Boundary Conditions (cont.)
17

 More boundary conditions for a method that finds a


specific target value in an array
 There is more than one occurrence of the target value
 The array has but one element and it is not the target
 The array has but one element and it is the target
 The array has no elements
Stubs and Drivers
Section 3.3
Stubs
 A stub is a replacement for a method not yet written
 The purpose of a stub is to allow early testing of
components already written. An example follows:
 /** Stub for method save.
 @pre the initial directory contents are read from a data
file.
 @post Writes the directory contents back to a data file.
 The boolean flag modified is reset to false.
 */
 public void save() {
 System.out.println("Stub for save has been called");
 modified = false;
 }
Stubs (cont.)
20

 A stub should print an identifying message


 It could also print the values of its input parameters
and any state variables that it may change
 Performing these operations allows the
programmer to follow the flow of control within
the client program is correct.
Preconditions and Postconditions
21

 Preconditions are the assumptions or constraints


upon the input data for the method
 They should be documented in a comment using
the @pre notation as in the preceding example
 Postconditions are any changes in state caused by
the function.
 These should be documented for all void methods
using the @post notation as in the preceding
example.
Drivers
22

 A driver is a testing tool which consists of a


program that creates any values and classes
necessary to test a method.
 After calling the method, it displays the results of
any output returned.
 Drivers are often conveniently executed as part of a
test framework such as JUnit
The JUnit Test Framework
Section 3.4
The JUnit Test Framework
24

 A test harness is a program written to test a method


or class
 It provides known inputs for a series of tests (the test
suite)
 It compares the results with known results and reports
whether the item under test passed or failed
 A test framework is a software product that
facilitates writing and running test suites.
 We will demonstrate how to use a test framework
named JUnit
Using JUnit
25

 Each test harness created in JUnit begins with two


import statements:
import org.junit.Test;
import static org.junit.Assert.*
 These allow us to use JUnit’s assert methods
 The assert methods allow us to specify pass/fail
behavior for tests.
 They are summarized in Table 3.2 page ???
JUnit Example
26

 Design of a JUnit program to test the


ArraySearch.search method.
 We wish to test the following:
 The target is the first element in the array
 The target is the last element in the array
 The target is somewhere in the middle
 The target is not in the array
 There is more than one occurrence of the target and we
find the first
JUnit Example (cont.)
27

 ArraySearch.search tests continues


 The array has only one element and it is not the target
 The array has only one element ant it is the target
 The array has no elements
 The entire listing for the JUnit program is
 Listing 3.1 on page ???
 The common array used for all tests:
// Common array to search for most of the tests
private final int[] x = {5, 12, 15, 4, 8, 12, 7};
JUnit Example (cont.)
28

 Testing the case where the target is the first element


@Test
public void firstElementTest() {
// Test for target as first element.
assertEquals("5 not at position 0",
0, ArraySearch.search(x, 5));
}

 The “assertEquals” method specifies the message


to print on failure, the expected result, and the
function call. We expect a return value of 0
because 5 is indeed in the first array element.
JUnit Example (cont.)
29

 Testing the case where the target is the last element


@Test
public void lastElementTest() {
// Test for target as last element.
assertEquals("7 not at position 6",
6, ArraySearch.search(x, 7));
}

 In this case, the target value was 7, and we expect


to find it in location 6 (the last element of the array)
JUnit Example (cont.)
30

 Testing the case where the target is somewhere in


the middle:
@Test
public void inMiddleTest() {
// Test for target somewhere in middle.
assertEquals("4 is not found at position 3",
3, ArraySearch.search(x, 4));
}

 Here, the target value was 4 and we expect to find


it in location 3.
JUnit Example (cont.)
31

 Testing the case where the target is not in the array


@Test
public void notInArrayTest() {
// Test for target not in array.
assertEquals(-1, ArraySearch.search(x, -5));
}

 Here, the target value was -5 and we expect a


return value of -1 indicating “not found.”
 The first parameter to assertEquals is omitted
which would result in a default failure message.
JUnit Example (cont.)
32

 Testing the case where the target is present in


multiple locations, we find the first
@Test
public void multipleOccurencesTest() {
// Test for multiple occurrences of target.
assertEquals(1, ArraySearch.search(x, 12));
}

 Target = 12, which occurs at locations 1 and 5. We


expect the program to return 1.
JUnit Example (cont.)
33

 Testing a 1 element array which does contain the


target value
@Test
public void oneElementArrayTestItemPresent() {
// Test for 1-element array
int[] y = {10};
assertEquals(0, ArraySearch.search(y, 10));
}

 We expect to find the 10 in location 0.


JUnit Example (cont.)
34

 Testing a 1 element array which does not contain


the target value
@Test
public void oneElementArrayTestItemAbsent() {
// Test for 1-element array
int[] y = {10};
assertEquals(-1, ArraySearch.search(y, -10));
}

 Y does not contain -10, so we expect a return value


of -1 meaning “not found.”
JUnit Example (cont.)
35

 Testing with an empty array


@Test
public void emptyArrayTest() {
// Test for an empty array
int[] y = new int[0];
assertEquals(-1, ArraySearch.search(y, 10));
}

 Y does not contain anything, so we expect a return


value of -1 meaning “not found.”
JUnit Example (cont.)
36

 Testing with a null pointer


@Test(expected=NullPointerException.class)
public void nullArrayTest() {
int[] y = null;
int i = ArraySearch.search(y, 10);
}

 Y is a null pointer. The @Test line says that the


test is successful if we get a NullPointerException
JUnit Example (cont.)
37

 The results of the tests are shown below:


Test Driven Development
Section 3.5
Test Driven Development
39

 Test driven development involves writing tests and


methods in parallel
 Write a test case for a new feature
 Run the test and note that it fails while other tests still
pass
 Make the minimum change necessary to make it pass
 Revise the code to remove any duplication
 Re-run the test to be sure it still passes
Case Study –
40
ArraySearch.search
 Test list
 The target element is not in the array
 The target element is the first in the array
 The target element is the last in the array
 There is more than one occurrence, we find the first
 The target is somewhere in the middle of the array
 The array has only one element
 The array has no elements
Case Study (cont.)
41

 Start with a stub


public class ArraySearch {
/**
* Search an array to find the first occurrence of a target
* @param x Array to search
* @param target Target to search for
* @return The subscript of the first occurrence if found:
* otherwise return -1
* @throws NullPointerException if x is null
*/
public static int search(int[] x, int target) {
return Integer.MIN_VALUE;
}
}
Case Study (cont.)
42

 Create a test (cases 1 and 6 above)


/**
* Test for ArraySearch class
* @author Koffman & Wolfgang
*/
public class ArraySearchTest {
@Test
public void itemNotFirstElementInSingleElementArray() {
int[] x = {5};
assertEquals(-1, ArraySearch.search(x, 10));
}
}
Case Study (cont.)
43

 The test fails (as we expected


Testcase: itemNotFirstElementInSingleElementArray:
FAILED
expected:<-1> but was:<-2147483648>
 We make the minimum change to let it pass
public static int search(int[] x, int target) {
return -1; // target not found
}
Case Study (cont.)
44

 Add a test for the target in the first location


@Test
public void itemFirstElementInSingleElementArray() {
int[] x = new int[]{5};
assertEquals(0, ArraySearch.search(x, 5));
}

 This again fails (as expected). We make a small change


public static int search(int[] x, int target) {
if (x[0] == target) {
return 0; // target found at 0
}
return -1; // target not found
}
Case Study (cont.)
45

 Now the first two tests pass. We notice a possible


improvement
 Returning 0 may not work when the array is larger, so we
make another small change
public static int search(int[] x, int target) {
int index = 0;
if (x[index] == target)
return index; // target at 0
return -1; // target not found
}

 The first two tests still pass with the new code
Case Study (cont.)
46

 Creating a new test, for an array of size 2


@Test
public void itemSecondItemInTwoElementArray() {
int[] x = {10, 20};
assertEquals(1, ArraySearch.search(x, 20));
}

 This fails (as expected) because we never actually look at the


second array element
Case Study (cont.)
47

 So we modify the code to make it pass


public static int search(int[] x, int target) {
int index = 0;
if (x[index] == target)
return index; // target at 0
index = 1;
if (x[index] == target)
return index; // target at 1
return -1; // target not found
}

 This now fails with an array of size 1


Case Study (cont.)
48

 We fix this by checking if index is too large


public static int search(int[] x, int target) {
int index = 0;
if (x[index] == target)
return index; // target at 0
index = 1;
if (index < x.length)
if (x[index] == target)
return index; // target at 1
return -1; // target not found
}

 And once again all tests pass


Case Study (cont.)
49

 Now we make a test for a still larger array


@Test
public void itemLastInMultiElementArray() {
int[] x = new int[]{5, 10, 15};
assertEquals(2, ArraySearch.search(x, 15));
}

 This fails if the target is anywhere beyond location 1


Case Study (cont.)
50

 So we add code to check the rest of the locations


public static int search(int[] x, int target) {
int index = 0;
if (x[index] == target)
return index; // target at 0
index = 1;
while (index < x.length) {
if (x[index] == target)
return index; // target at index
index++;
}
return -1; // target not found
}

 This now passes all of our tests. However…


Case Study (cont.)
51

 We add a test to check that it returns -1 for an empty array


@Test
public void itemNotInEmptyArray() {
int[] x = new int[0];
assertEquals(-1, ArraySearch.search(x, 5));
}

 This fails, because our test for index < x.length comes too
late.
Case Study (cont.)
52

 Examining our code, we note that we can fix this error and
make the code shorter at the same time by eliminating the
special case test location 0.
} public static int search(int[] x, int target) {
int index = 0;
while (index < x.length) {
if (x[index] == target)
return index; // target at index
index++;
}
return -1; // target not found
}

 This version now passes all of our tests


Testing Interactive Programs
Section 3.6
Interactive Programs and JUnit
54

 The text contains a program that solicits the user


for an integer in a specific range. We want to use
JUnit to test such a program.
 One advantage of a test framework is that it is
automated and all of the input is specified in
advance. How can we apply this to a program that
demands user input?
Interactive Programs and Junit
55
(cont.)
 The solution lies in the use of
ByteArrayInputStream and
ByteArrayOutputStream.
 ByteArrayInputStream is a form of InputStream
that consists of a fixed array of bytes.
Interactive Programs and Junit
56
(cont.)
 The following test code provides the string “3” to
the program being tested just as if it had been typed
by a human user
@Test
public void testForNormalInput() {
ByteArrayInputStream testIn =
new ByteArrayInputStream("3".getBytes());
System.setIn(testIn);
int n = MyInput.readInt("Enter weight", 2, 5);
assertEquals(n, 3);
}
Interactive Programs and Junit
57
(cont.)
 To capture the prompt, we need to create a
ByteArrayOutputStream that can be filled in by the
programs System.out.print statements
@Test
public void testForNormalInput() {
ByteArrayInputStream testIn =
new ByteArrayInputStream("3".getBytes());
System.setIn(testIn);
int n = MyInput.readInt("Enter weight", 2, 5);
assertEquals(n, 3);
}
Interactive Programs and Junit
58
(cont.)
 This does the job
@Test
public void testThatPromptIsCorrectForNormalInput() {
ByteArrayInputStream testIn =
new ByteArrayInputStream("3".getBytes());
System.setIn(testIn);
ByteArrayOutputStream testOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(testOut));
int n = MyInput.readInt("Enter weight", 2, 5);
assertEquals(n, 3);
String displayedPrompt = testOut.toString();
String expectedPrompt = "Enter weight" +
"\nEnter an integer between 2 and 5" + NL;
assertEquals(expectedPrompt, displayedPrompt);
}
Interactive Programs and Junit
59
(cont.)
 The line that reads
String expectedPrompt = "Enter weight" +
"\nEnter an integer between 2 and 5" + NL;
 Assumes that NL has been given a meaning. NL is
meant to represent the newline character and this is
system specific. To find what it is for your system
you can add
private static final String NL =
System.getProperty("line.separator");
Debugging
Section 3.7
Debugging
61

 Debugging is like detective work


 You must search for clues in the output information
your program gives you.
 Sometimes, you need to ask, temporarily for more
output than your program is currently providing
Debugging (cont.)
62

 The loop below does not terminate when the user


enters the sentinel string (“***”).
public static String getSentence() {
Scanner in = new Scanner(System.in);
StringBuilder stb = new StringBuilder();
int count = 0;
while (count < 10) {
System.out.println("Enter a word or *** to
quit");
String word = in.next();
if (word == "***") break;
stb.append(word);
}
Debugging (cont.)
63

 To better understand the problem, you add the


following line of code as the first statement of the
loop body. This will display the progressive
sequence of words entered.
System.out.println("!!! Next word is " + word + ", count
is " + count);
Debugging (cont.)
64

 Running this will show you that *** does appear, but
does not trigger the loop exit. This suggests that
there is something wrong with the loop conditition.
 The problem is that the condition word == “***” is
comparing addresses. The correct while condition is
while (word != null && !word.equals("***") && count < 10)
Using a Debugger
65

 Most IDE’s contain debugging tools


 These allow you to execute your code incrementally,
to set points where execution will stop and allow you
to look at the contents of various memory cells.
Using a Debugger
66

 In Netbeans, for instance before running your


program you can set a “breakpoint,” a line where
your program will pause.
 You do this by clicking on the vertical bar to the left
of the statement where you want to pause.

 The next slide contains a screenshot of a Netbeans


showing the values of variables while paused at a
breakpoint
Using a Debugger (cont.)
67
Using a Debugger (cont.)
68

 The use of a debugger in an IDE such as Netbeans


can substantially speed up the process of finding
bugs over the use of temporary print statements.
 However, no debugging tool can replace a
thoughtful, logical approach to zeroing in on coding
errors.

You might also like