Python Testing & Debugging: pdb, unittest & Test-Driven Development

1. What is testing and debugging in Python?

Q: What are testing and debugging?

Testing verifies that code behaves correctly, while debugging identifies and fixes errors. Python supports automated testing for reliability and interactive debugging for troubleshooting.

Testing: Ensures functions/classes work as expected, catching bugs early.

Debugging: Inspects code execution to find issues like logic errors or crashes.

Use Case: Ensuring a data processing script handles edge cases correctly.

2. What is pdb, and how do you use the Python debugger?

Q: What is pdb?

pdb (Python Debugger) is Python's built-in interactive debugger for stepping through code, setting breakpoints, and inspecting variables.

Key Commands:

Use Case: Debugging a function with unexpected output.

3. Can you give an example of using pdb?

import pdb

def calculate_average(numbers):
    pdb.set_trace()  # Breakpoint here
    total = sum(numbers)
    avg = total / len(numbers)
    return avg  # Bug: len(numbers) = 0 causes error

# Test with empty list
result = calculate_average([])
print(f"Average: {result}")

Note: pdb.set_trace() sets a breakpoint. Use n (next) to step through; fix the bug by adding if not numbers: return 0.

4. What is unit testing with unittest in Python?

Q: What is unittest?

unittest is Python's built-in module for writing and running tests, verifying individual units (e.g., functions) work correctly.

Key Components:

Running: python3 -m unittest test_file.py.

Use Case: Testing a function's output for different inputs.

5. Can you give an example of unit testing with unittest?

# Function to test
def add(a, b):
    return a + b

# Test class
import unittest

class TestAddFunction(unittest.TestCase):
    def setUp(self):
        print("Setting up test...")

    def tearDown(self):
        print("Tearing down test...")

    def test_add_positive(self):
        self.assertEqual(add(2, 3), 5)

    def test_add_negative(self):
        self.assertEqual(add(-1, 1), 0)

    def test_add_zero(self):
        self.assertEqual(add(0, 0), 0)

if __name__ == "__main__":
    unittest.main()

Note: assertEqual verifies expected vs. actual results. setUp/tearDown run before/after each test. Run with python3 unittest_example.py for verbose output.

6. What is Test-Driven Development (TDD) in Python?

Q: What is TDD?

TDD is a software development process where tests are written before the code, following a red-green-refactor cycle:

Benefits: Ensures code reliability, clarifies requirements, and reduces bugs.

Use Case: Developing a function by first defining its expected behavior via tests.

7. Can you give an example of TDD basics?

# TDD: Test-Driven Development example

# Step 1: Red - Write failing test
import unittest

class TestCalculator(unittest.TestCase):
    def test_multiply_positive(self):
        self.assertEqual(multiply(2, 3), 6)  # Fails initially

# Step 2: Green - Write minimal code to pass
def multiply(a, b):
    return a * b

# Step 3: Refactor - Improve code
def multiply(a, b):
    """Multiply two numbers."""
    if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
        raise TypeError("Inputs must be numbers")
    return a * b

# Run tests
if __name__ == "__main__":
    unittest.main()

Note: Start with a failing test (Red). Implement minimal code to pass (Green). Refactor with type checks while keeping tests passing. Run with python3 tdd_example.py.

8. Common Mistakes & Best Practices

Q: Common mistakes?

Q: Best practices?