ویژگی تصویر

کتابخانه mock در پایتون

  /  پایتون   /  کتابخانه mock در پایتون
بنر تبلیغاتی الف

کتابخانه Mock در پایتون یکی از ابزارهای بسیار مهم برای تست نرم‌افزار است که به برنامه‌نویسان اجازه می‌دهد تا بخش‌هایی از کد را که به منابع خارجی وابسته‌اند، شبیه‌سازی کنند. این کتابخانه به‌ویژه در تست‌های واحد (Unit Tests) مفید است و معمولاً به همراه ماژول unittest استفاده می‌شود.

Mock در اصل به شما کمک می‌کند تا بدون نیاز به اجرای واقعی کدهای وابسته (مثل پایگاه‌داده، API یا فایل‌ها)، رفتار آنها را تقلید کنید. به این ترتیب، تست‌های شما سریع‌تر، قابل‌اعتمادتر و مستقل از محیط بیرونی خواهند بود.

مقدمه‌ای بر مفهوم Mocking

در برنامه‌نویسی، گاهی بخشی از کد به سرویس‌هایی مانند بانک اطلاعاتی، شبکه یا فایل‌سیستم متصل است. تست کردن این بخش‌ها به‌صورت واقعی کار سختی است، چون ممکن است:

  • نیاز به اتصال به اینترنت داشته باشند.
  • به داده‌های واقعی وابسته باشند.
  • کند یا غیرقابل پیش‌بینی باشند.

در چنین شرایطی، از mocking استفاده می‌شود تا یک شیء جعلی (fake object) بسازیم که مانند شیء واقعی رفتار می‌کند ولی کنترل کامل آن در دست ماست. کتابخانه unittest.mock این قابلیت را در پایتون فراهم می‌کند.

نحوه نصب و استفاده از Mock

در نسخه‌های جدید پایتون (از 3.3 به بعد)، کتابخانه Mock در داخل ماژول استاندارد unittest قرار دارد و نیازی به نصب جداگانه ندارد. فقط کافی است آن را import کنید:

from unittest.mock import Mock

در نسخه‌های قدیمی‌تر پایتون (قبل از 3.3)، باید آن را نصب کنید:

pip install mock

ساخت یک Mock ساده

برای شروع، یک نمونه ساده از Mock ایجاد می‌کنیم و رفتار آن را بررسی می‌کنیم:

from unittest.mock import Mock

# ایجاد یک شیء mock
my_mock = Mock()

# تنظیم مقدار برگشتی برای یک تابع
my_mock.get_data.return_value = "Hello, Python!"

# فراخوانی تابع mock
result = my_mock.get_data()

print(result)

در این مثال، تابع get_data() در واقع وجود ندارد، اما با استفاده از Mock آن را شبیه‌سازی کرده‌ایم تا مقدار خاصی برگرداند. این قابلیت در تست‌های واحد بسیار مفید است، چون بدون نیاز به اجرای واقعی کد می‌توان رفتار آن را بررسی کرد.

بررسی فراخوانی‌ها در Mock

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

my_mock = Mock()
my_mock.process_data(10, 20)

# بررسی اینکه آیا تابع با آرگومان خاصی فراخوانی شده است
my_mock.process_data.assert_called_with(10, 20)

# بررسی اینکه تابع فقط یک بار فراخوانی شده است
my_mock.process_data.assert_called_once()

در اینجا، اگر تابع با پارامترهای متفاوتی فراخوانی شده باشد، تست شکست می‌خورد. این روش به شما کمک می‌کند تا از درستی منطق کد اطمینان حاصل کنید.

استفاده از Patch برای جایگزینی اشیاء واقعی

ماژول Mock دارای ابزاری به نام patch است که می‌تواند موقتاً یک شیء واقعی را در هنگام تست جایگزین کند. این ویژگی برای تست توابعی که با منابع خارجی کار می‌کنند بسیار مفید است.

from unittest.mock import patch

def get_user_data():
    import requests
    response = requests.get("https://api.example.com/user")
    return response.json()

# تست تابع بدون ارسال درخواست واقعی
@patch("requests.get")
def test_get_user_data(mock_get):
    mock_get.return_value.json.return_value = {"name": "Matin", "age": 25}

    result = get_user_data()
    assert result["name"] == "Matin"
    assert result["age"] == 25

در این مثال، با استفاده از @patch، تابع requests.get موقتاً جایگزین می‌شود تا نیازی به اتصال واقعی به اینترنت نباشد. این رویکرد در تست API‌ها بسیار کاربردی است.

استفاده از patch به صورت context manager

به‌جای دکوراتور، می‌توانید از patch در قالب with نیز استفاده کنید:

from unittest.mock import patch

with patch("os.remove") as mock_remove:
    import os
    os.remove("file.txt")

    mock_remove.assert_called_with("file.txt")

در این حالت، در محدوده بلوک with، تابع واقعی os.remove با Mock جایگزین می‌شود و پس از اتمام بلوک، به حالت اصلی برمی‌گردد.

تفاوت Mock و MagicMock

کتابخانه Mock شامل کلاس دیگری به نام MagicMock نیز هست که علاوه بر ویژگی‌های Mock، متدهای جادویی پایتون (مثل __len__، __getitem__، و غیره) را هم پشتیبانی می‌کند.

from unittest.mock import MagicMock

mock_list = MagicMock()
mock_list.__len__.return_value = 5

print(len(mock_list))  # Output: 5

در اینجا، با استفاده از MagicMock می‌توان طول یک لیست فرضی را کنترل کرد، در حالی که با Mock عادی این امکان به‌صورت مستقیم وجود ندارد.

ویژگی‌های پرکاربرد Mock

ویژگیتوضیحمثال
return_valueمقداری که باید هنگام فراخوانی تابع بازگرددmock.method.return_value = 10
side_effectتعیین رفتار ویژه در هنگام اجرا (مثل خطا یا چند مقدار مختلف)mock.method.side_effect = ValueError("Error!")
call_argsآخرین آرگومان‌هایی که تابع با آنها فراخوانی شده استmock.method.call_args
assert_called_with()بررسی اینکه تابع با پارامتر خاصی فراخوانی شده باشدmock.method.assert_called_with(x, y)

استفاده از side_effect برای کنترل رفتار

پارامتر side_effect در Mock می‌تواند برای شبیه‌سازی خطاها یا مقادیر مختلف در فراخوانی‌های پیاپی استفاده شود.

from unittest.mock import Mock

mock = Mock()
mock.calculate.side_effect = [10, 20, 30]

print(mock.calculate())
print(mock.calculate())
print(mock.calculate())

در این مثال، هر بار که تابع calculate فراخوانی شود، یکی از مقادیر مشخص‌شده در side_effect بازگردانده می‌شود.

همچنین می‌توانید از آن برای شبیه‌سازی استثنا (exception) استفاده کنید:

mock.fetch_data.side_effect = Exception("Connection failed")

در این حالت، هر بار که تابع fetch_data فراخوانی شود، استثنای مشخص‌شده رخ می‌دهد.

بهترین روش‌ها (Best Practices) در استفاده از Mock

  • فقط بخش‌هایی را mock کنید که واقعاً لازم است (مثل درخواست‌های HTTP، دسترسی به فایل، یا پایگاه‌داده).
  • از patch در سطح ماژول استفاده کنید، نه در کلاس یا تابع داخلی.
  • از نام‌های واضح برای mockها استفاده کنید تا خوانایی تست‌ها افزایش یابد.
  • پس از اتمام تست، همیشه رفتار mock را بررسی کنید (با استفاده از assert_called_*).
  • از MagicMock فقط زمانی استفاده کنید که واقعاً نیاز به متدهای جادویی دارید.

جمع‌بندی

کتابخانه Mock ابزاری بسیار قدرتمند برای تست و شبیه‌سازی در پایتون است. این کتابخانه به شما اجازه می‌دهد وابستگی‌های بیرونی را کنترل و شبیه‌سازی کنید، تا تست‌های شما سریع‌تر و پایدارتر اجرا شوند. با درک درست از ویژگی‌هایی مانند patch، return_value و side_effect می‌توانید تست‌های حرفه‌ای و دقیق‌تری بنویسید.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: