Python
  • Intro.
  • Catalogue
  • Chapter 1: Introduction to Python
  • Chapter 2: Python Syntax and Fundamentals
    • Chapter: Variables and Data Types in Python
  • Chapter 3: Control Flow
  • Chapter 4: Functions
  • Chapter 5: Data Structures
  • Chapter 6: Object-Oriented Programming (OOP)
  • Chapter 7: Modules and Packages
  • Chapter 8: File Handling
  • Chapter 9: Error and Exception Handling
  • Chapter 10: Working with Databases
  • Chapter 11: Iterators and Generators
  • Chapter 12: Decorators and Context Managers
  • Chapter 13: Concurrency and Parallelism
  • Chapter 14: Testing and Debugging
  • Chapter 15: Web Development with Python
  • Chapter 16: Data Science and Machine Learning with Python
  • Chapter 17: Working with APIs
  • Chapter 18: Automation with Python
  • Chapter 19: Python and Cloud/DevOps
  • Chapter 20: Python and IoT
  • Appendices
Powered by GitBook
On this page

Chapter 14: Testing and Debugging

Testing and debugging are critical aspects of software development. Testing ensures your code works as intended, while debugging helps identify and resolve errors. Python offers robust tools and libraries to facilitate these tasks.

Unit Testing

Unit testing involves testing individual components of your program (e.g., functions or classes) to verify their correctness. The unittest module is Python's built-in framework for unit testing.

Writing Test Cases

  1. Import the unittest module.

  2. Create a test class that inherits from unittest.TestCase.

  3. Define test methods starting with test_.

  4. Use assertion methods to check expected outcomes.

Example:

import unittest

def add(a, b):
    return a + b

class TestMathFunctions(unittest.TestCase):
    def test_add(self):
        self.assertEqual(add(2, 3), 5)
        self.assertEqual(add(-1, 1), 0)

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

Mocking

Mocking simulates the behavior of complex objects or external systems during testing. The unittest.mock module provides powerful tools for mocking.

Example:

from unittest.mock import Mock

# Mocking a function
mock = Mock(return_value=10)
print(mock())  # Output: 10

# Mocking a method
mock.method = Mock(return_value="Hello")
print(mock.method())  # Output: Hello

Debugging

Python provides several tools for debugging, including the pdb module, logging, and IDE debuggers.

Using pdb (Python Debugger):

  1. Insert import pdb; pdb.set_trace() in your code where you want to set a breakpoint.

  2. Use commands like n (next), s (step), and c (continue) to navigate.

Example:

import pdb

def divide(a, b):
    pdb.set_trace()  # Set a breakpoint
    return a / b

print(divide(10, 2))

Using logging for Debugging:

The logging module records messages to help debug and monitor your program.

Example:

import logging

logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")

def multiply(a, b):
    logging.debug(f"Multiplying {a} and {b}")
    return a * b

print(multiply(3, 4))

Types of Testing

Type

Description

Unit Testing

Tests individual components or functions.

Integration Testing

Verifies that different parts of the application work together.

System Testing

Tests the complete application as a whole.

Acceptance Testing

Validates the application against business requirements.

Test-Driven Development (TDD)

In TDD, you write tests before writing the actual code. The workflow is:

  1. Write a failing test.

  2. Write code to make the test pass.

  3. Refactor the code.

Example:

  1. Write a test:

    def test_is_even():
        assert is_even(4) == True
        assert is_even(5) == False
  2. Write code:

    def is_even(num):
        return num % 2 == 0
  3. Refactor if needed.

Continuous Testing

Automate tests to run continuously using tools like pytest and CI/CD pipelines.

Example with pytest:

  1. Install pytest: pip install pytest

  2. Write tests in a file named test_<name>.py.

  3. Run tests: pytest.

Example:

# test_math.py
def add(a, b):
    return a + b

def test_add():
    assert add(2, 3) == 5
    assert add(-1, 1) == 0

Exercises

Exercise 1:

Write a test case to verify the functionality of a reverse_string function.

Solution:

import unittest

def reverse_string(s):
    return s[::-1]

class TestStringFunctions(unittest.TestCase):
    def test_reverse_string(self):
        self.assertEqual(reverse_string("hello"), "olleh")
        self.assertEqual(reverse_string("Python"), "nohtyP")

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

Exercise 2:

Mock an API call to return a predefined value during testing.

Solution:

from unittest.mock import Mock

def fetch_data():
    return "Real Data"

mock_fetch_data = Mock(return_value="Mock Data")
print(mock_fetch_data())  # Output: Mock Data

Exercise 3:

Use pytest to test a function that calculates the factorial of a number.

Solution:

# test_factorial.py
def factorial(n):
    if n == 0 or n == 1:
        return 1
    return n * factorial(n - 1)

def test_factorial():
    assert factorial(5) == 120
    assert factorial(0) == 1

Best Practices

  1. Write clear, concise, and comprehensive test cases.

  2. Use mocking to isolate the component being tested.

  3. Automate tests to ensure frequent execution.

  4. Use meaningful log messages for debugging.

  5. Follow TDD to improve code quality and maintainability.

In the next chapter, we will explore web development with Python, focusing on frameworks like Flask and Django, and creating APIs.

PreviousChapter 13: Concurrency and ParallelismNextChapter 15: Web Development with Python

Last updated 5 months ago