Welcome to the World of Testing and Debugging!
Ever written a piece of code, clicked "Run," and... nothing happened? Or worse, it did something completely weird? Don't worry—every single programmer, from beginners to experts at Google, faces this! Testing and Debugging are the "detective skills" of computing. In this chapter, we will learn how to find mistakes (bugs), how to fix them, and how to prevent them from happening in the first place. Let’s get started!
1. Types of Program Errors
Before we can fix a problem, we need to know what kind of problem it is. There are three main types of errors you will encounter:
Syntax Errors
Think of this as a "grammar mistake" in code. If you misspell a keyword like prnt() instead of print(), or forget a colon at the end of an if statement, the computer won't even start running your program. It simply doesn't understand your instructions.
Run-time Errors
The program starts running, but suddenly "crashes" while it is working. A common example is trying to divide a number by zero \( (x / 0) \) or trying to open a file that doesn't exist. The instructions make sense, but the computer hits a "brick wall" it can't climb over.
Logic Errors
These are the trickiest! The program runs perfectly without crashing, but the output is wrong. For example, if you want to calculate \( 2 + 2 \) but accidentally type 2 * 2, the computer gives you 4. It did exactly what you told it to do, but you gave it the wrong instructions! Logic errors are found by the programmer, not the computer.
Quick Review:
• Syntax: Computer can't understand the "language."
• Run-time: Computer "trips and falls" while running.
• Logic: Computer follows instructions perfectly, but the instructions are wrong.
2. Finding the "Bugs" (Debugging Techniques)
How do we find where things went wrong? Here are the most effective tools in your detective kit:
The Manual Dry Run and Trace Tables
A manual dry run is when you sit down with a pen and paper and act like the computer. You follow the code line-by-line and record how the values of variables change. We use a Trace Table to keep track of this.
Example: If you have a loop that adds numbers, your trace table columns would be the "Loop Counter" and the "Total Variable." This helps you see exactly at which step the logic fails.
Using Print Statements
Sometimes you need "X-ray vision" to see what’s happening inside your program. By inserting print() statements at different steps, you can check if your variables hold the values you expect.
Tip: If your program stops working halfway, add a print("Step 1 reached") to see how far it gets!
Backtracking
If you notice an error at the very end of your program, backtracking means looking at the code backwards from the point where the error was spotted to find the original source of the mistake.
Incremental Testing and Commenting Out
Incremental testing means writing a small piece of code and testing it before moving on to the next part. Don't write 100 lines at once! If you have a large block of code that isn't working, you can comment out (using the # symbol in Python) parts of the program to isolate the section that is causing the trouble.
Key Takeaway: Don't try to find the error just by staring at the screen. Use trace tables for logic, print statements for "live" data, and test your code in small chunks!
3. Data Validation: Checking the Input
There is a famous saying in computing: "Garbage In, Garbage Out" (GIGO). If a user enters "banana" when your program expects an "age," your program will break. Data Validation is a check performed by the computer to ensure the data entered is "sensible" or "valid" before it is processed.
Common Validation Checks:
• Presence check: Ensures that data has actually been entered (the field isn't left blank).
• Range check: Checks that a number is within certain limits (e.g., an age must be between 0 and 120).
• Length check: Checks if the input has the correct number of characters (e.g., a password must be at least 8 characters).
• Format check: Ensures the data matches a specific pattern (e.g., a postal code like "123456").
• Existence check: Checks if the data already exists in the system (e.g., checking if a username is already taken).
• Check digit: A special extra digit added to a number (like an ISBN or barcode) used to verify that the number was entered correctly using a mathematical formula.
What to do with invalid data?
• For interactive input (where a user is typing): The program should display an error message and ask for the input again.
• For non-interactive input (like reading from a file): The program should usually exit or log an error because it cannot ask a file for "better" data.
4. Designing Test Cases
To be 100% sure your program works, you need to test it with different types of data. We call these Test Cases. Imagine you are testing a program that accepts exam marks from 0 to 100.
1. Normal Data
Data that should be accepted without any issues.
Example: 25, 50, 85.
2. Boundary (Extreme) Data
Data at the "edges" of what is allowed. This is where logic errors often hide!
Example: 0 and 100 (the minimum and maximum allowed).
3. Error (Invalid) Data
Data that the program should reject. This tests if your validation is working.
Example: -5, 105, "abc".
Did you know? Many bugs happen because programmers forget to test the boundary data. They test if "50" works, but forget to check if the program accepts exactly "0" or "100"!
Quick Review Box:
When designing tests, always pick:
1. Something in the middle (Normal)
2. The exact limits (Boundary)
3. Something totally wrong (Error)
Summary Checklist
Don't worry if this seems like a lot! Just remember these core points:
• Use Trace Tables to step through code line-by-line.
• Syntax errors are grammar mistakes; Logic errors are result mistakes.
• Validation stops "garbage" data from entering your program.
• Always test with Normal, Boundary, and Error data.
• Use print() statements to see what your variables are doing while the program runs!