MENU

[AI from Scratch] Episode 239: Testing and Debugging Essentials — Techniques for Maintaining Code Quality

TOC

Recap and Today’s Theme

Hello! In the previous episode, we explored version control with Git, covering the basics of code management and team collaboration. Using Git allows developers to manage project history, develop with multiple branches, and foster a cooperative development environment.

Today, we will discuss testing and debugging, which are crucial for maintaining code quality. Integrating tests during development ensures the stability and quality of your code, while effective debugging helps identify and fix issues efficiently. This article introduces basic testing techniques and effective debugging methods, enabling you to enhance code quality and minimize bugs.

The Basics of Testing

Testing verifies that software behaves as expected and helps prevent issues early. By incorporating testing into development, you can maintain the stability and quality of your code.

1. Types of Tests

There are several types of tests, but the following three are the most common:

  • Unit Test: Tests the smallest parts of a program (e.g., functions or methods) individually. It focuses on specific details, helping to catch bugs early.
  • Integration Test: Combines multiple units to verify interactions between modules, such as API calls or database connections.
  • End-to-End (E2E) Test: Tests the entire system, simulating real user scenarios. Commonly used for web or mobile applications.

2. Importance of Testing

Testing offers several benefits:

  • Improved Code Quality: Unit and integration tests help detect bugs early and ensure code works as expected.
  • Development Efficiency: Automated tests quickly verify whether new features affect existing code.
  • Safe Refactoring: Tests provide a safety net when restructuring code, ensuring that changes do not introduce new bugs.

Implementing Unit Tests with Python

Python’s standard library includes unittest, making it easy to implement unit tests. Below is a simple example demonstrating how to write basic unit tests.

1. The Code to Be Tested (calculator.py)

def add(x, y):
    return x + y

def subtract(x, y):
    return x - y

def multiply(x, y):
    return x * y

def divide(x, y):
    if y == 0:
        raise ValueError("Division by zero!")
    return x / y

2. Writing Unit Tests (test_calculator.py)

import unittest
from calculator import add, subtract, multiply, divide

class TestCalculator(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(1, 2), 3)
        self.assertEqual(add(-1, 1), 0)

    def test_subtract(self):
        self.assertEqual(subtract(5, 3), 2)

    def test_multiply(self):
        self.assertEqual(multiply(2, 3), 6)

    def test_divide(self):
        self.assertEqual(divide(10, 2), 5)
        with self.assertRaises(ValueError):
            divide(10, 0)

if __name__ == '__main__':
    unittest.main()
  • self.assertEqual(): Verifies that the expected result matches the actual result.
  • self.assertRaises(): Tests whether a specific error (in this case, ValueError) is raised.

3. Running the Tests

Run the tests in the terminal with:

python test_calculator.py

If all tests pass, the code works as expected. If a test fails, it indicates which part of the code needs fixing.

Automating Tests and Using CI/CD

Manually running tests is tedious and prone to oversight. Implement automated testing and use CI/CD tools to automate testing whenever code changes.

1. Automated Testing Tools

  • pytest: A Python testing framework that allows for concise test writing and provides various features. Install it with pip install pytest and run tests with the pytest command.
  • tox: Automates testing across multiple Python environments, simplifying tests with different versions and dependencies.

2. Setting Up CI/CD

Platforms like GitHub and GitLab provide CI/CD tools like GitHub Actions and GitLab CI that run tests automatically whenever code is pushed.

Example .github/workflows/test.yml for GitHub Actions:

name: Python Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install pytest
    - name: Run tests
      run: pytest

This configuration runs tests automatically whenever changes are pushed to the repository, ensuring continuous integration and quality control.

Debugging Basics and Efficient Techniques

Even with tests, bugs may still occur. Effective debugging is essential for identifying and fixing issues quickly. Here are some basic debugging methods.

1. Using Logs

Logs provide real-time information about code execution. In Python, you can use the logging module to record logs.

import logging

logging.basicConfig(level=logging.DEBUG)

def divide(x, y):
    logging.debug(f"divide called with: x={x}, y={y}")
    if y == 0:
        logging.error("Division by zero!")
        raise ValueError("Division by zero!")
    return x / y
  • logging.debug(): Records detailed information, typically used during development.
  • logging.error(): Logs error messages, making it easier to pinpoint issues.

2. Using the Debugger (pdb)

Python’s built-in debugger, pdb, allows for interactive code execution and variable inspection.

import pdb

def add(x, y):
    pdb.set_trace()  # Sets a breakpoint
    return x + y

add(2, 3)
  • pdb.set_trace(): Pauses code execution at this point, enabling step-by-step inspection and interaction.

3. Using IDE Debugging Features

Integrated Development Environments (IDEs) like VSCode and PyCharm offer powerful debugging tools. By setting breakpoints, you can trace code execution efficiently, making it easier to identify bugs.

Best Practices for Testing and Debugging

  1. Test Frequently in Small Increments: Run tests whenever you make changes, focusing on small units to catch bugs early.
  2. Automate Testing: Implement CI/CD tools to run tests automatically on every code change.
  3. Prioritize Reproducible Bugs: Identify conditions that reproduce bugs and use those cases to fix issues.
  4. Comprehensive Logging: Log relevant information to quickly identify and fix problems.

Summary

In this episode, we covered the essentials of testing and debugging, outlining techniques for maintaining code quality. Testing improves code quality, and effective debugging helps fix bugs efficiently. Automating tests and integrating CI/CD further streamlines development, allowing for high-quality code delivery.

Next Episode Preview

Next time, we will conclude Chapter 8 with a review and understanding check, summarizing the concepts we’ve covered so far and ensuring a solid understanding. Let’s review what we’ve learned to prepare for the next steps!


Notes

  • Unit Test: Tests the smallest units of code individually.
  • CI/CD: Stands for Continuous Integration/Continuous Delivery, a system that automatically tests and deploys code changes.
Let's share this post !

Author of this article

株式会社PROMPTは生成AIに関する様々な情報を発信しています。
記事にしてほしいテーマや調べてほしいテーマがあればお問合せフォームからご連絡ください。
---
PROMPT Inc. provides a variety of information related to generative AI.
If there is a topic you would like us to write an article about or research, please contact us using the inquiry form.

Comments

To comment

TOC