۱۵

Testing و Debugging 🧪

یاد می‌گیریم چطور کدهامون رو تست کنیم و باگ‌ها رو شکار کنیم!

🧪 Unit Testing: تست کردن قطعه به قطعه

Unit Testing یعنی اینکه هر تابع و کلاس رو جداگانه تست کنیم تا مطمئن بشیم درست کار می‌کنن. پایتون یه ماژول داخلی به اسم `unittest` داره که خیلی قدرتمنده!

مثال ساده Unit Test
import unittest

# کلاسی که می‌خوایم تستش کنیم
class Calculator:
    def add(self, a, b):
        return a + b
    
    def divide(self, a, b):
        if b == 0:
            raise ValueError("تقسیم بر صفر مجاز نیست!")
        return a / b
    
    def is_even(self, number):
        return number % 2 == 0

# کلاس تست
class TestCalculator(unittest.TestCase):
    def setUp(self):
        """این متد قبل از هر تست اجرا می‌شه"""
        self.calc = Calculator()
    
    def test_add_positive_numbers(self):
        """تست جمع اعداد مثبت"""
        result = self.calc.add(2, 3)
        self.assertEqual(result, 5)
    
    def test_add_negative_numbers(self):
        """تست جمع اعداد منفی"""
        result = self.calc.add(-2, -3)
        self.assertEqual(result, -5)
    
    def test_divide_normal(self):
        """تست تقسیم عادی"""
        result = self.calc.divide(10, 2)
        self.assertEqual(result, 5.0)
    
    def test_divide_by_zero(self):
        """تست تقسیم بر صفر - باید خطا بده"""
        with self.assertRaises(ValueError):
            self.calc.divide(10, 0)
    
    def test_is_even(self):
        """تست تشخیص عدد زوج"""
        self.assertTrue(self.calc.is_even(4))
        self.assertFalse(self.calc.is_even(5))

# اجرای تست‌ها
if __name__ == '__main__':
    unittest.main()

# خروجی:
# .....
# ----------------------------------------------------------------------
# Ran 5 tests in 0.001s
# 
# OK

دیباگینگ: شکار باگ‌ها! 🐛

دیباگینگ یعنی پیدا کردن و رفع کردن باگ‌ها. پایتون ابزارهای قدرتمندی برای این کار داره:

  • print() debugging: ساده‌ترین روش برای دیدن مقادیر متغیرها
  • pdb debugger: دیباگر داخلی پایتون برای کنترل دقیق اجرای کد
  • logging: ثبت اطلاعات مفصل برای تحلیل بعدی
import pdb
import logging

# تنظیم logging
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s')

def divide_numbers(a, b):
    logging.debug(f"شروع تقسیم {a} بر {b}")
    
    # نقطه توقف برای دیباگ
    pdb.set_trace()
    
    if b == 0:
        logging.error("تقسیم بر صفر!")
        raise ValueError("نمی‌توان بر صفر تقسیم کرد")
    
    result = a / b
    logging.info(f"نتیجه: {result}")
    return result

# تست
try:
    result = divide_numbers(10, 2)
    print(f"نتیجه: {result}")
except ValueError as e:
    print(f"خطا: {e}")

تست‌های پیشرفته: pytest و Mock

pytest یکی از محبوب‌ترین کتابخانه‌های تست پایتون هست. Mock هم برای شبیه‌سازی اجزای خارجی استفاده می‌شه:

import pytest
from unittest.mock import Mock, patch
import requests

# کلاس برای تست
class WeatherAPI:
    def get_temperature(self, city):
        response = requests.get(f"http://api.weather.com/{city}")
        return response.json()['temperature']

# تست با pytest
def test_calculator_add():
    calc = Calculator()
    assert calc.add(2, 3) == 5
    assert calc.add(-1, 1) == 0

def test_calculator_divide_by_zero():
    calc = Calculator()
    with pytest.raises(ValueError):
        calc.divide(10, 0)

# تست با Mock
@patch('requests.get')
def test_weather_api(mock_get):
    # شبیه‌سازی پاسخ API
    mock_response = Mock()
    mock_response.json.return_value = {'temperature': 25}
    mock_get.return_value = mock_response
    
    weather = WeatherAPI()
    temp = weather.get_temperature('Tehran')
    
    assert temp == 25
    mock_get.assert_called_once_with('http://api.weather.com/Tehran')

# اجرای تست‌ها:
# pytest test_file.py -v

تمرین! 🧠

یه کلاس `BankAccount` بنویس که متدهای `deposit`, `withdraw` و `get_balance` داشته باشه. بعد برای این کلاس تست‌های کامل بنویس که شامل تست موجودی اولیه، واریز پول، برداشت پول و برداشت بیش از موجودی (که باید خطا بده) باشه. از unittest استفاده کن.

جواب تمرین

import unittest

class BankAccount:
    def __init__(self, initial_balance=0):
        self.balance = initial_balance
    
    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("مبلغ باید مثبت باشد")
        self.balance += amount
        return self.balance
    
    def withdraw(self, amount):
        if amount <= 0:
            raise ValueError("مبلغ باید مثبت باشد")
        if amount > self.balance:
            raise ValueError("موجودی کافی نیست")
        self.balance -= amount
        return self.balance
    
    def get_balance(self):
        return self.balance

class TestBankAccount(unittest.TestCase):
    def setUp(self):
        self.account = BankAccount(100)
    
    def test_initial_balance(self):
        self.assertEqual(self.account.get_balance(), 100)
    
    def test_deposit(self):
        self.account.deposit(50)
        self.assertEqual(self.account.get_balance(), 150)
    
    def test_withdraw(self):
        self.account.withdraw(30)
        self.assertEqual(self.account.get_balance(), 70)
    
    def test_withdraw_insufficient_funds(self):
        with self.assertRaises(ValueError):
            self.account.withdraw(200)
    
    def test_negative_deposit(self):
        with self.assertRaises(ValueError):
            self.account.deposit(-10)

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

چالش پیشرفته! 🚀

یه سیستم تست کامل برای یه فروشگاه آنلاین بساز که شامل کلاس‌های Product, Cart و Order باشه. تست‌هات باید شامل اضافه کردن محصول به سبد، محاسبه قیمت کل، اعمال تخفیف و پردازش سفارش باشه. از Mock برای شبیه‌سازی پرداخت آنلاین استفاده کن!