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:
b line: Set breakpoint at line.s (step): Step into functions.n (next): Step over lines.c (continue): Continue to next breakpoint or end.p var: Print variable value.q (quit): Exit debugger.
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:
TestCase: Base class for tests.assertEqual(expected, actual): Checks equality.setUp()/tearDown(): Setup/teardown before/after each test.
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:
- Red: Write a failing test.
- Green: Write minimal code to pass the test.
- Refactor: Improve code while keeping tests passing.
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?
- pdb Debugger: Not setting breakpoints, missing execution flow. Using
pdb.set_trace()in production code. Forgetting to quit (q) or continue (c), hanging the program. - unittest: Writing tests after code, missing edge cases. Not using
setUp/tearDownfor shared setup. Overusing assertions, making tests brittle. - TDD: Writing overly detailed tests, slowing development. Not refactoring, leading to code smells. Ignoring test failures, accumulating bugs.
- General: Not testing with real data or edge cases. Debugging in production, risking data integrity.
Q: Best practices?
- pdb Debugger: Use
b linefor breakpoints;p varfor inspection. Combine with IDE debuggers (e.g., VS Code, PyCharm) for GUI support. Removepdb.set_trace()before deployment. - unittest: Follow AAA pattern (Arrange, Act, Assert) for clear tests. Use
setUpfor common initialization. Run tests frequently withpython3 -m unittest. - TDD: Write small, focused tests first. Refactor after passing tests to improve design. Aim for 80%+ test coverage.
- General: Use pytest for more advanced testing (beyond unittest). Integrate with CI/CD (e.g., GitHub Actions) for automated runs. Document tests with comments for clarity. Test error conditions (e.g., invalid inputs) explicitly.