CS 261 Lab C - Debugging
Due Wed Sep 17 at 3:00pm
Overview
Finding and removing errors in programs ("debugging") is probably the most common, most important, and most difficult task in implementing computer programs. Luckliy, there are plenty of tried and true methods to help identify errors, and Eclipse is able to offer further support.
- Fun fact: The word "debug" was coined by Dr. Grace Hopper when she was working on the Mark II computer back in 1947. A moth was discovered inside the computer, interfering with a relay and keeping it from working. Removing the moth ("debugging", as Dr. Hopper called it) fixed the computer.
This lab will walk you through the debugging process and how to use the Eclipse debugger, and give you a chance to practice and experiment with it. Note that while you may be able to find and fix the bugs on your own, you should try and use the debugger so that you are familiar with the Eclipse tools (which will incredibly helpful later!).
This lab will be completed in pairs. Partner assignments for this lab can be found on Moodle
- Remember to review the pair programming guidelines before you get started!
- In the end both partners should to understand how to use the debugger!
Objectives
- To learn to use the Eclipse Debugger
- To practice writing test cases for regression testing
- To review and practice debugging techniques
Necessary Files
You will need to download the copy of the zipped
source files
and import them into Eclipse (see previous lab for instructions). These files contain SampleProgram.java
, a set of sample code to practice using the Debugger; StringProcessor.java
a somewhat buggy class that you will need to fix; and StringProcessorTester
, the beginnings of a tester class for you to fill in. I've also provided a bugger PrimeFactorization
class as an extension.
Background: Test Cases
The first step to fixing a program that doesn't work is realizing that the the program doesn't work. We do this by testing the program. Testing has 3 steps:
- Figure out what the result of the program should be
- Run the program
- Check if the result of the program matches step 1
Step 1 is really important--do not skip it! In order to know if a program works, you need to know what it is supposed to do. Think about this beforehand--work through a program in your head or on paper, so that you know what the answer is supposed to be.
In order to test that a particular method works, you want to create a number of test cases. A test case is a specific input you will give to a program (e.g., what values you will pass as parameters) as well as the expected output of the program. For example, if we had an add()
method that added two numbers together, then we might have test cases such as:
Input: add(1, 1); Expected Output: 2
Testing a program involves coming up with a list of test cases, writing a Tester class that runs those cases, and then seeing if they work or not. If a test input didn't produce the output you expected, then you have found an error! For example, if we ran our method add(1, 1)
and got 3
as the output, then we know there is a problem.
The trick is to come up with good test cases, so you can be sure your program works no matter what the input is. One way to write good test cases is to be sure and test boundary conditions. These are the "edge cases" where things may be different. For example, our add method might test when we have two positive numbers, two negative numbers, one positive and one negative, and where one or both of the numbers are 0. We also might test with really small numbers and really big numbers, in each spot. The key is to get good coverage of all the possibly inputs to our method.
- For "search" methods, come with test cases for where the method will find one occurance, more than one occurance, and no occurances of the item you are searching for!
- Remember to test "bad" input; words that have non-letters in them, null items, etc. While you can often expect the "user" to provide valid input, it's always a good idea to handle bad input!
Your goal is to try your darndest to break the program. If you succeed, you've found a bug and are a winner!
Background: Print Statements for Debugging
The above quote from Brian Kernighan (one of the inventors of the C language) remains true in Java--print statement debugging can be one of the most effective methods of debugging a program. Or at least one of your professors' favorite methods. The basic idea is thus: in order to figure out whether a program is working, we need to know what the program is doing. Which means we want to know things like the value of variables at any particular time, whether or not we've gotten into a particular method or loop, etc. This can be fairly intuitive process--basically if you want to know about something your program is doing, just put a print statement to tell you!
Some hints for doing print-statement debugging
-
Put the print statement as close as possible to the problem code (generally right before the line that breaks); this will let you see what the program was doing right before it crashed.
- "in methodName", "inside for loop" "inside if statement" are also good things to print out.
- I like putting
"***DEBUG***"
in big letters before my output String; it makes it easier to see the debug messages if you have a lot of output being printed. - Be sure and comment out and delete your print statements once you're finished!
Part 1: The Eclipse Debugger
The main goal of using print statement debugging is to be able to "inspect" the current state of the program as it is running--to get a sense for what code is being executed when and how often, and what the values of variables are at any particular moment.
However, this kind of inspection can be automed with Eclipse's Debugger, a tool built into the IDE.
Despite the name, the debugger won't actually fix bugs for you--instead, it is a tool that helps you to "walk" through your program running each line of code one at a time, in order to inspect what the code and variables are doing at any particular moment. The debugger will not locate the logical error for you. You need to be able to understand the code and its intention, using the debugger to observe the program's behavior. That said, the debugger can be a very handy tool!
Your first task will be to follow a tutorial and learn how to use Eclipse's debugger. Because we are interested in re-use in CS, I am going to re-use a tutorial written by Deb Deppeler at UW-Madison. This tutorial can be found at:
http://pages.cs.wisc.edu/~cs302/?r=labs&labNumber=7
You should follow the tutorial and complete the step labeled "Task 1: Learning How Eclipse Helps do Debugging". You will need to use the SampleProgram
code that you downloaded with the lab files.
- And if you need any help, please ask me!
Part 2: Testing and Debugging
Once you understand how the Debugger works, your second task is to debug the included StringProcessor
class. This class has 4 methods--plus the getter, setter, and constructor. Most if not all the methods have bugs (usually more than one!).
You should debug this class using the following procedure:
-
Write a test case for the
StringProcessor
inside theStringProcessorTester
. This test case should call one of the methods and print out the expected and actual result of the method.- Be sure and check all the boundary conditions you and your partner can think of, so you can be sure and catch all the errors!
-
When you identify an error, use the Eclipse Debugger to inspect the code as it is running, setting appropriate break points as needed
- While you may be able to fix the bugs with print statements or even just by considering the error, I highly recommend you practice with the debugger. While it may seem a tad overkill for this lab, it will quickly become helpful in the future.
- Once you have identified the error, fix the bug!
-
Re-run your
StringProcessorTester
class and confirm that the bug is fixed!-
DO NOT DELETE TESTS THAT PASS!
Leave all tests in your
StringProcessorTester
, so that ypou can confirm that methods continue to work even if you change other parts of the class. This will also allow us to see what test cases you use (which is part of the lab grade). -
If things get messy, you can always comment out your test cases momentarily, or refactor them into individual methods that get called from main (e.g.,
public static void testMethod1()
)
-
DO NOT DELETE TESTS THAT PASS!
Leave all tests in your
- Return to step 1 and repeat until you have thoroughly tested the class.
Try to catch all the bugs--there are a lot (and some are more tricky than others). Gotta catch 'em all! Extra credit will be given to the pair that finds the most bugs.
- Your tester should demonstrate that the methods all work flawlessly, because you've fixed everything.
Extensions
If you finish early, you might try debugging the PrimeFactorization
class found in the lab files. It isn't necessarily harder to fix, just another thing to try to make sure you're really comfortable with the tool.
Submission
Make sure both your names are on the StringProcessor
and the StringProcessorTester
. Once you're finished, upload the src code to the LabC submission folder on hedwig. One partner should to upload the code--we will only grade one partner's copy of the program. Make sure you upload your work to the correct folder! The lab is due at the start of class the day after lab.
After you have submitted your solution, log onto Moodle and submit the Lab C Partner Evaluation. Both partners need to submit evaluations.
Grading
This assignment will be graded out of 24 points:
- [17pt] Each method in
StringProcessor
works flawlessly--that is, you have found all the bugs! - [2pt] Your
StringProcessorTester
contains all your test cases - [4pt] Your test cases demonstrate good coverage of the individual methods
- [1pt] You have completed your lab partner evaluation