کتابخانه 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 میتوانید تستهای حرفهای و دقیقتری بنویسید.
آیا این مطلب برای شما مفید بود ؟




