Introduction to Tracing, Debugging, and Testing
Writing code is an exciting process, but even the most experienced programmers rarely get it right on the first try! In this chapter, we explore how to find and fix mistakes, how to follow the "logic" of a program step-by-step, and how to prove that our solution actually works. These skills are a core part of Theme B: Computational Thinking and Problem-Solving and are essential for your Internal Assessment (the computational solution) and Paper 2 exams.
Don't worry if your code doesn't work immediately—debugging is where the real learning happens!
1. Tracing Code (The "Dry Run")
Tracing is the process of manually following the execution of a program's logic, one line at a time, to see how the values of variables change. We often call this a dry run because we are acting like a "human computer" without actually running the code on a machine.
Trace Tables
The most effective tool for tracing is a Trace Table. This is a grid where each column represents a variable, and each row represents a step in the program or a change in a variable's value.
Example Trace Table Scenario:
Imagine a loop that calculates the sum of numbers from 1 to 3.
1. \( total = 0 \)
2. Loop \( i \) from 1 to 3:
3. \( total = total + i \)
Trace Table:
- Line 1: \( total = 0 \), \( i = null \)
- Line 2 (Iteration 1): \( total = 0 \), \( i = 1 \)
- Line 3 (Iteration 1): \( total = 1 \), \( i = 1 \)
- Line 2 (Iteration 2): \( total = 1 \), \( i = 2 \)
- Line 3 (Iteration 2): \( total = 3 \), \( i = 2 \)
- Line 2 (Iteration 3): \( total = 3 \), \( i = 3 \)
- Line 3 (Iteration 3): \( total = 6 \), \( i = 3 \)
Quick Tip: In an exam, if you are asked to trace code, always use a table. It keeps your thoughts organized and prevents simple calculation errors!
Key Takeaway:
Tracing helps you understand why a program is behaving a certain way and is the best way to catch logic errors before you even touch a keyboard.
2. Debugging: Finding and Fixing Errors
Debugging is the systematic process of identifying, isolating, and fixing "bugs" (errors) in your code. To be a good debugger, you first need to know what kind of error you are looking for.
Types of Errors
- Syntax Errors: These are "grammar" mistakes in the code. If you forget a colon in Python or a semicolon in Java, the computer won't understand how to run the code. The program will not start at all.
- Runtime Errors: The code is written correctly (no syntax errors), but something impossible happens while it is running. Example: Trying to divide a number by zero \( (x / 0) \) or trying to access the 10th item in a list that only has 5 items. These cause the program to "crash."
- Logic Errors: These are the trickiest! The program runs without crashing, but it gives the wrong output. This happens because the programmer's instructions were logically flawed. Example: Using \( > \) instead of \( >= \) in a loop condition.
Common Debugging Techniques
- Print Debugging: Inserting "print" statements (e.g., \( print(x) \) in Python or \( System.out.println(x) \) in Java) at various points in the code to see what the variables are doing in real-time.
- Integrated Development Environment (IDE) Tools: Most modern tools allow you to set breakpoints (pausing the code at a specific line) and step through the code line-by-line to watch the variables change in a "watch window."
- Rubber Ducking: Explaining your code out loud, line by line, to an object (like a rubber duck). Often, as you explain what a line should do, you realize it isn't actually doing that!
Key Takeaway:
Syntax errors stop the code from starting; runtime errors crash it while running; logic errors let it run but produce incorrect results. Use print statements or breakpoints to find where things go wrong.
3. Testing Strategies
Testing is different from debugging. While debugging is about fixing a known problem, testing is about finding problems and proving that the program meets the success criteria defined in the computational thinking process.
Types of Test Data
To thoroughly test a program, you must use different categories of data. Let's imagine we are testing a program that only accepts ages between 13 and 19 (inclusive).
- Normal Data: Data that is clearly within the expected range. Example: \( 15 \). The program should accept this.
- Boundary Data (Extreme Data): Data at the very limits of what is acceptable. Example: \( 13 \) and \( 19 \). These are often where logic errors occur (e.g., using \( > \) instead of \( >= \)).
- Invalid Data (Erroneous Data): Data that is outside the range or of the wrong type. Example: \( 5 \), \( 99 \), or even a string like \( "hello" \). The program should reject these and ideally provide an error message (see the chapter on Input Validation for more).
The Testing Cycle
Testing isn't just a one-time event. As part of Theme B: Computational Thinking, you should:
1. Design a test plan with expected outcomes.
2. Run the tests with your selected data.
3. Compare the actual output to the expected output.
4. Evaluate and improve the code if the results don't match.
Key Takeaway:
A good test plan includes Normal, Boundary, and Invalid data to ensure the program is robust and reliable.
Summary Quick Review
Tracing: Manual step-by-step check using a trace table.
Errors: Syntax (grammar), Runtime (crash), Logic (wrong answer).
Debugging: Fixing errors using print statements or IDE tools.
Testing: Proving the code works using Normal, Boundary, and Invalid data.
Note: For more information on how to prevent errors during the data entry phase, please refer to the chapter on File handling and input validation.