Python Coding Academy
135 subscribers
7 photos
3 files
4 links
🐍 Learn Python. Build Skills. Create the Future.

📚 Python lessons from beginner to advanced
💻 Clear explanations & real-world examples
🚀 Learn to code. Learn to build.
Download Telegram
🐍 آموزش جامع Python — پست ۲۵۶
محدودکردن Mock با spec، spec_set و autospec
در پست قبل دیدیم که Mock می‌تواند ویژگی‌های ساختگی ایجاد کند؛ اما این انعطاف‌پذیری گاهی مشکل‌ساز می‌شود.
برای مثال، ممکن است نام یک متد را اشتباه بنویسیم و Mock بدون هشدار آن را ایجاد کند. در نتیجه، تست با وجود اشتباه در کد همچنان موفق شود!
برای جلوگیری از این مشکل، سه قابلیت مهم داریم.
🔹 ۱. استفاده از spec
با spec مشخص می‌کنیم Mock باید از چه ساختاری پیروی کند.
from unittest.mock import Mock


class User:
def get_name(self):
return "Sara"


user = Mock(spec=User)

user.get_name()

# خطا:
user.get_email()

چون get_email در کلاس User تعریف نشده است، دسترسی به آن خطا ایجاد می‌کند.
🔹 ۲. استفاده از spec_set
spec_set محدودیت بیشتری اعمال می‌کند.
from unittest.mock import Mock


class User:
def get_name(self):
return "Sara"


user = Mock(spec_set=User)

user.get_name()

# خطا:
user.email = "sara@example.com"

در این حالت، علاوه بر جلوگیری از دسترسی به ویژگی‌های تعریف‌نشده، نمی‌توان ویژگی جدیدی به Mock اضافه کرد.
🔹 ۳. استفاده از autospec
قابلیت autospec علاوه بر بررسی ساختار، امضای متدها (Signature) را نیز در نظر می‌گیرد.
from unittest.mock import create_autospec


class Calculator:
def add(self, a, b):
return a + b


calculator = create_autospec(
Calculator,
instance=True
)

calculator.add(10, 20)

# خطا: تعداد آرگومان‌ها اشتباه است
calculator.add(10)

در این مثال، autospec اجازه نمی‌دهد متد add() با تعداد آرگومان‌های نامعتبر فراخوانی شود.
🔍 مقایسه سه قابلیت

⚠️ نکات مهم
spec و spec_set برای جلوگیری از اشتباه در نام ویژگی‌ها مفید هستند.
autospec برای بررسی دقیق‌تر ساختار و امضای فراخوانی‌ها مناسب است.
autospec ممکن است در کلاس‌هایی که ویژگی‌های آن‌ها فقط هنگام اجرای __init__() ساخته می‌شوند، تمام ویژگی‌های نمونه را تشخیص ندهد.
هیچ‌کدام جایگزین تست رفتار واقعی برنامه نیستند.
🎯 تمرین
کلاس زیر را در نظر بگیر:
class Product:
def get_price(self, discount):
return 100 - discount

با create_autospec() یک نمونه ساختگی ایجاد کن و موارد زیر را آزمایش کن:
۱. فراخوانی get_price(20) باید مجاز باشد.
۲. فراخوانی get_price() باید خطا ایجاد کند.
۳. فراخوانی متد تعریف‌نشده get_stock() باید خطا ایجاد کند.
هدف این تمرین، ساخت Mockهایی است که به ساختار واقعی برنامه نزدیک‌تر باشند.
پست ۲۵۶ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۲۵۷
مدیریت چندین Mock با patch.multiple()
در پروژه‌های بزرگ ممکن است یک ماژول چندین وابستگی خارجی داشته باشد. اگر بخواهیم همه آن‌ها را هنگام تست جایگزین کنیم، استفاده از چندین patch() جداگانه می‌تواند کد را طولانی کند.
در چنین شرایطی می‌توانیم از patch.multiple() استفاده کنیم.
🔹 ۱. آشنایی با patch.multiple()
این ابزار امکان جایگزینی چند ویژگی از یک ماژول یا شیء را به‌صورت هم‌زمان فراهم می‌کند.
فرض کنید فایل services.py را داریم:
def send_email(message):
print("Email:", message)


def send_sms(message):
print("SMS:", message)


def notify_user(message):
send_email(message)
send_sms(message)

می‌خواهیم هنگام تست، هر دو سرویس ارسال را شبیه‌سازی کنیم.
🔹 ۲. نوشتن تست
import unittest
from unittest.mock import patch, DEFAULT

import services


class TestNotification(unittest.TestCase):

@patch.multiple(
services,
send_email=DEFAULT,
send_sms=DEFAULT
)
def test_notify(self, send_email, send_sms):

services.notify_user("Welcome!")

send_email.assert_called_once_with("Welcome!")
send_sms.assert_called_once_with("Welcome!")


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

🔍 بررسی کد
patch.multiple()
چند ویژگی از ماژول services را به‌طور هم‌زمان جایگزین می‌کند.
DEFAULT
به patch.multiple() می‌گوید برای این ویژگی، یک Mock پیش‌فرض ایجاد کند.
send_email و send_sms
در این مثال، Mockهای ایجادشده به‌عنوان آرگومان به متد تست تحویل داده می‌شوند.
assert_called_once_with()
بررسی می‌کند هر سرویس دقیقاً یک بار با آرگومان مورد انتظار فراخوانی شده باشد.
🔹 ۳. استفاده با with
می‌توانیم به‌جای Decorator از ساختار with نیز استفاده کنیم:
with patch.multiple(
services,
send_email=DEFAULT,
send_sms=DEFAULT
) as mocks:

services.notify_user("Welcome!")

mocks["send_email"].assert_called_once()
mocks["send_sms"].assert_called_once()

در این روش، دیکشنری mocks شامل Mockهای ساخته‌شده است.
⚠️ نکته مهم
جایگزینی‌ها فقط در محدوده اجرای Patch فعال هستند.
پس از خروج از بلوک with یا پایان تست، ویژگی‌های اصلی بازگردانده می‌شوند.
اگر فقط یک وابستگی دارید، معمولاً patch() ساده‌تر است.
اگر نام ویژگی‌ها از قبل مشخص باشد، DEFAULT باعث ساخت خودکار Mock می‌شود.
🎯 تمرین
ماژولی با سه تابع زیر در نظر بگیر:
save_user()
send_email()
send_notification()

تابعی به نام register_user() بنویس که هر سه عملیات را اجرا کند.
سپس با patch.multiple() هر سه وابستگی را جایگزین کن و بررسی کن که هرکدام دقیقاً یک بار فراخوانی شده‌اند.
پست ۲۵۷ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۲۵۸
مدیریت موقت تغییرات با patch.dict()
در تست‌نویسی گاهی لازم است مقادیر یک دیکشنری را موقتاً تغییر دهیم؛ مثلاً متغیرهای محیطی (Environment Variables)، تنظیمات برنامه یا داده‌های پیکربندی.
برای این کار می‌توانیم از patch.dict() در ماژول unittest.mock استفاده کنیم.
🔹 ۱. آشنایی با patch.dict()
فرض کنید برنامه ما تنظیمات را از یک دیکشنری دریافت می‌کند:
settings = {
"DEBUG": False,
"LANGUAGE": "en"
}

می‌خواهیم هنگام اجرای تست، مقدار DEBUG را به True تغییر دهیم؛ بدون اینکه تنظیمات اصلی برای همیشه تغییر کنند.
from unittest.mock import patch

settings = {
"DEBUG": False,
"LANGUAGE": "en"
}

with patch.dict(settings, {"DEBUG": True}):
print(settings["DEBUG"])

print(settings["DEBUG"])

خروجی:
True
False

پس از پایان بلوک with، وضعیت اولیه دیکشنری بازیابی می‌شود.
🔹 ۲. استفاده برای تست متغیرهای محیطی
یکی از کاربردهای مهم این ابزار، مدیریت متغیرهای محیطی با os.environ است.
فرض کنید تابع زیر را داریم:
import os


def get_app_mode():
return os.getenv("APP_MODE", "development")

حالا می‌توانیم بدون تغییر دائمی تنظیمات سیستم، آن را آزمایش کنیم:
import unittest
import os
from unittest.mock import patch

from app import get_app_mode


class TestAppMode(unittest.TestCase):

def test_production_mode(self):
with patch.dict(
os.environ,
{"APP_MODE": "production"}
):
self.assertEqual(
get_app_mode(),
"production"
)

def test_default_mode(self):
with patch.dict(os.environ, {}, clear=True):
self.assertEqual(
get_app_mode(),
"development"
)


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

🔍 بررسی پارامترها
آرگومان اول: دیکشنری موردنظر برای تغییر.
آرگومان دوم: مقادیری که باید موقتاً اضافه یا جایگزین شوند.
clear=True: ابتدا محتوای دیکشنری را پاک می‌کند و سپس مقادیر جدید را قرار می‌دهد.
در تست دوم، از clear=True استفاده کردیم تا مطمئن شویم متغیر APP_MODE از قبل در محیط وجود ندارد و نتیجه تست به تنظیمات سیستم وابسته نیست.
⚠️ نکات مهم
۱. تغییرات patch.dict() پس از پایان محدوده Patch بازگردانده می‌شوند.
۲. این ابزار برای دیکشنری‌های معمولی و نگاشت‌هایی مانند os.environ کاربرد دارد.
۳. اگر چند تست هم‌زمان به یک متغیر محیطی دسترسی داشته باشند، تغییرات موقت ممکن است باعث تداخل شوند؛ بنابراین مدیریت تست‌های موازی اهمیت دارد.
۴. clear=True تمام مقادیر قبلی را در محدوده Patch حذف می‌کند؛ پس هنگام استفاده از آن دقت کنید.
🎯 تمرین
تابعی به نام get_database_url() بنویس که مقدار متغیر محیطی DATABASE_URL را برگرداند.
اگر این متغیر وجود نداشت، مقدار زیر را برگرداند:
sqlite:///app.db

سپس با patch.dict() دو تست بنویس:
وقتی DATABASE_URL مقدار مشخصی دارد، همان مقدار برگردد.
وقتی این متغیر وجود ندارد، مقدار پیش‌فرض برگردد.
پست ۲۵۸ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۵۹
مدیریت رفتار Mock با side_effect
در تست‌نویسی، گاهی لازم است یک تابع ساختگی (Mock) در هر فراخوانی رفتار متفاوتی داشته باشد. برای این کار از ویژگی side_effect استفاده می‌کنیم.
این ویژگی سه کاربرد مهم دارد:
۱. شبیه‌سازی وقوع خطا
می‌توانیم مشخص کنیم که فراخوانی Mock یک استثنا ایجاد کند:
from unittest.mock import Mock

api = Mock(side_effect=TimeoutError("Connection timed out"))

try:
api()
except TimeoutError as error:
print(error)

خروجی:
Connection timed out

۲. برگرداندن پاسخ‌های متوالی
می‌توانیم یک لیست به side_effect بدهیم تا هر فراخوانی، مقدار بعدی را برگرداند:
from unittest.mock import Mock

get_number = Mock(side_effect=[10, 20, 30])

print(get_number())
print(get_number())
print(get_number())

خروجی:
10
20
30

هر بار که Mock فراخوانی می‌شود، یک مقدار از لیست مصرف می‌شود. اگر مقادیر تمام شوند، StopIteration رخ می‌دهد.
۳. اجرای تابع سفارشی
می‌توانیم یک تابع واقعی را به‌عنوان side_effect تعیین کنیم:
from unittest.mock import Mock

def calculate(number):
return number * 2

mock_function = Mock(side_effect=calculate)

print(mock_function(5))
print(mock_function(10))

خروجی:
10
20

در این حالت، آرگومان‌های ارسال‌شده به Mock به تابع side_effect منتقل می‌شوند.
تفاوت return_value و side_effect
return_value: یک مقدار ثابت برای فراخوانی‌های Mock تعیین می‌کند.
side_effect: امکان ایجاد خطا، برگرداندن مقادیر متوالی یا اجرای منطق سفارشی را فراهم می‌کند.
نکته: اگر side_effect یک تابع باشد، می‌تواند مقدار ویژه DEFAULT را برگرداند تا Mock از return_value استفاده کند.
🎯 تمرین
یک Mock برای شبیه‌سازی درخواست به سرور بسازید که:
در فراخوانی اول خطای ConnectionError ایجاد کند.
در فراخوانی دوم مقدار "Success" را برگرداند.
با استفاده از try/except خطای اول را مدیریت کنید.
پست ۲۵۹ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۶۰
استفاده از patch.object() در تست‌نویسی
در پست‌های قبلی با patch() آشنا شدیم. یکی از ابزارهای مرتبط با آن، patch.object() است که برای جایگزینی موقت یک ویژگی یا متد روی یک شیء یا کلاس استفاده می‌شود.
۱. جایگزینی موقت یک متد
فرض کنید کلاسی داریم که اطلاعات کاربر را از یک سرویس دریافت می‌کند:
class UserService:
def get_username(self):
return "Ali"

می‌توانیم در تست، متد get_username را موقتاً با یک مقدار ساختگی جایگزین کنیم:
import unittest
from unittest.mock import patch

class UserService:
def get_username(self):
return "Ali"

class TestUserService(unittest.TestCase):

def test_username(self):
service = UserService()

with patch.object(
service,
"get_username",
return_value="TestUser"
):
self.assertEqual(
service.get_username(),
"TestUser"
)

self.assertEqual(service.get_username(), "Ali")

در این مثال:
داخل بلوک with، متد مقدار "TestUser" را برمی‌گرداند.
پس از پایان بلوک، رفتار اصلی متد بازگردانده می‌شود.
۲. جایگزینی ویژگی یک کلاس
patch.object() فقط مخصوص متدها نیست و می‌تواند ویژگی‌های کلاس را نیز موقتاً تغییر دهد:
from unittest.mock import patch

class Config:
DEBUG = False

with patch.object(Config, "DEBUG", True):
print(Config.DEBUG)

print(Config.DEBUG)

خروجی:
True
False

۳. تفاوت patch() و patch.object()
ابزار
کاربرد
patch()
جایگزینی یک شیء با استفاده از مسیر ماژولی
patch.object()
جایگزینی ویژگی یا متد یک شیء یا کلاس مشخص
نکته مهم: هر دو ابزار پس از پایان محدوده اجرا، مقدار یا رفتار اولیه را بازمی‌گردانند؛ بنابراین برای تغییرات موقت در تست‌ها بسیار کاربردی هستند.
🎯 تمرین
کلاسی به نام Calculator بسازید که متد add() داشته باشد.
سپس با استفاده از patch.object() این متد را طوری جایگزین کنید که به‌جای جمع دو عدد، همیشه مقدار 100 را برگرداند.
بررسی کنید که پس از پایان patch.object()، متد دوباره رفتار اصلی خود را داشته باشد.
پست ۲۶۰ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۶۱
مدیریت محدوده Patch با start() و stop()
تا اینجا از patch() و patch.object() بیشتر در قالب with استفاده کردیم. اما گاهی لازم است کنترل شروع و پایان یک Patch را به‌صورت دستی در اختیار داشته باشیم.
برای این کار از متدهای start() و stop() استفاده می‌کنیم.
۱. استفاده از start()
با فراخوانی start()، جایگزینی موقت آغاز می‌شود:
from unittest.mock import patch

class Database:
def get_user(self):
return "Ali"

db = Database()

patcher = patch.object(
db,
"get_user",
return_value="TestUser"
)

patcher.start()

print(db.get_user())

خروجی:
TestUser

۲. استفاده از stop()
برای بازگرداندن رفتار اصلی، باید stop() را فراخوانی کنیم:
patcher.stop()

print(db.get_user())

خروجی:
Ali

با اجرای stop()، جایگزینی موقت حذف می‌شود.
۳. مدیریت Patch در تست‌های unittest
اگر در چند تست به یک Patch نیاز داشته باشیم، می‌توانیم آن را در setUp() فعال کنیم و با addCleanup() مطمئن شویم که در پایان تست متوقف می‌شود:
import unittest
from unittest.mock import patch

class Database:
def get_user(self):
return "Ali"


class TestDatabase(unittest.TestCase):

def setUp(self):
self.db = Database()

patcher = patch.object(
self.db,
"get_user",
return_value="TestUser"
)

patcher.start()
self.addCleanup(patcher.stop)

def test_get_user(self):
self.assertEqual(
self.db.get_user(),
"TestUser"
)


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

چرا addCleanup() مهم است؟
چون اگر در طول تست خطایی رخ دهد، پاک‌سازی ثبت‌شده همچنان اجرا می‌شود و Patch در محیط تست باقی نمی‌ماند.
نکات مهم
هر start() باید در نهایت با stop() متوقف شود.
فراموش‌کردن stop() ممکن است روی تست‌های بعدی تأثیر بگذارد.
برای مدیریت خودکار محدوده Patch، استفاده از with معمولاً ساده‌تر و ایمن‌تر است.
در تست‌های unittest، ترکیب start() و addCleanup() برای مدیریت دستی بسیار مناسب است.
🎯 تمرین
یک کلاس به نام PaymentService بسازید که متد get_status() آن مقدار "Pending" را برگرداند.
سپس با patch.object() و استفاده از start()، مقدار آن را به "Paid" تغییر دهید و در پایان با stop() رفتار اصلی را بازگردانید.
پست ۲۶۱ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۶۲
بررسی فراخوانی‌های Mock با assert_has_calls()
در تست‌نویسی، گاهی فقط بررسی خروجی یک تابع کافی نیست؛ باید مطمئن شویم متدهای وابسته نیز با آرگومان‌های درست و به ترتیب مورد انتظار فراخوانی شده‌اند.
برای این کار از assert_has_calls() استفاده می‌کنیم.
۱. بررسی چند فراخوانی
ابتدا یک Mock می‌سازیم و چند بار آن را با آرگومان‌های مختلف فراخوانی می‌کنیم:
from unittest.mock import Mock, call

send_message = Mock()

send_message("Hello")
send_message("Welcome")
send_message("Goodbye")

send_message.assert_has_calls([
call("Hello"),
call("Welcome"),
call("Goodbye")
])

اگر فراخوانی‌ها مطابق انتظار باشند، تست موفق می‌شود؛ در غیر این صورت AssertionError رخ می‌دهد.
۲. بررسی ترتیب فراخوانی‌ها
به‌صورت پیش‌فرض، assert_has_calls() ترتیب فراخوانی‌ها را بررسی می‌کند.
send_message.assert_has_calls([
call("Hello"),
call("Welcome")
])

این بررسی موفق است، چون این دو فراخوانی با همین ترتیب در تاریخچه Mock وجود دارند.
اما اگر ترتیب را تغییر دهیم، ممکن است تست شکست بخورد:
send_message.assert_has_calls([
call("Welcome"),
call("Hello")
])

۳. نادیده گرفتن فراخوانی‌های اضافی
پارامتر any_order=True اجازه می‌دهد ترتیب فراخوانی‌ها مهم نباشد:
send_message.assert_has_calls(
[
call("Goodbye"),
call("Hello")
],
any_order=True
)

در این حالت، وجود فراخوانی‌های موردنظر بررسی می‌شود؛ ترتیب آن‌ها اهمیتی ندارد.
نکته: به‌صورت پیش‌فرض، فراخوانی‌های مورد انتظار باید به‌صورت متوالی در تاریخچه فراخوانی‌ها وجود داشته باشند؛ اما با any_order=True ترتیب بررسی نمی‌شود.
تفاوت با ابزارهای قبلی
assert_called_once_with() یک فراخوانی با آرگومان مشخص را بررسی می‌کند.
assert_any_call() بررسی می‌کند که یک فراخوانی مشخص حداقل یک‌بار اتفاق افتاده باشد.
assert_has_calls() چند فراخوانی را به‌صورت مجموعه‌ای بررسی می‌کند.
🎯 تمرین
یک Mock به نام logger بسازید و سه پیام مختلف به آن ارسال کنید.
سپس با assert_has_calls() بررسی کنید که دو پیام اول با ترتیب صحیح فراخوانی شده‌اند.
پست ۲۶۲ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۶۳
بررسی تعداد فراخوانی‌های Mock
در تست‌نویسی، گاهی لازم است علاوه بر بررسی خروجی، تعداد دفعات اجرای یک تابع یا متد را نیز کنترل کنیم.
کتابخانه unittest.mock چند ابزار برای این کار ارائه می‌دهد.
۱. ویژگی call_count
این ویژگی تعداد دفعات فراخوانی یک Mock را مشخص می‌کند:
from unittest.mock import Mock

send_email = Mock()

send_email()
send_email()
send_email()

print(send_email.call_count)

خروجی:
3

۲. متد assert_called_once()
بررسی می‌کند که Mock دقیقاً یک‌بار فراخوانی شده باشد:
send_email = Mock()

send_email()

send_email.assert_called_once()

اگر Mock صفر بار یا بیشتر از یک بار فراخوانی شده باشد، تست با AssertionError شکست می‌خورد.
۳. متد assert_not_called()
بررسی می‌کند که Mock اصلاً فراخوانی نشده باشد:
send_email = Mock()

send_email.assert_not_called()

اگر حتی یک‌بار فراخوانی شده باشد، این بررسی شکست می‌خورد.
۴. متد assert_called()
بررسی می‌کند که Mock حداقل یک‌بار فراخوانی شده باشد:
send_email = Mock()

send_email()

send_email.assert_called()

۵. بررسی تعداد فراخوانی در یک تست واقعی
import unittest
from unittest.mock import Mock

class TestNotification(unittest.TestCase):

def test_send_notification(self):
notify = Mock()

notify("User 1")
notify("User 2")

self.assertEqual(notify.call_count, 2)
notify.assert_called()

self.assertEqual(
notify.call_args_list[0].args,
("User 1",)
)


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

در این تست:
تعداد فراخوانی‌ها باید دقیقاً ۲ باشد.
Mock حداقل یک‌بار اجرا شده باشد.
اولین فراخوانی باید آرگومان "User 1" را دریافت کرده باشد.
نکته مهم
تفاوت این ابزارها:
ابزار
کاربرد
call_count
دریافت تعداد فراخوانی‌ها
assert_called()
حداقل یک فراخوانی
assert_called_once()
دقیقاً یک فراخوانی
assert_not_called()
بدون هیچ فراخوانی
🎯 تمرین
یک Mock به نام save_data بسازید و آن را چهار بار فراخوانی کنید.
سپس با استفاده از call_count و assert_called() تعداد و وضعیت فراخوانی‌ها را بررسی کنید.
پست ۲۶۳ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۶۴
بازنشانی Mock با reset_mock()
در تست‌نویسی ممکن است بخواهیم یک Mock را چند بار استفاده کنیم، اما اطلاعات مربوط به فراخوانی‌های قبلی آن روی تست جدید تأثیر نگذارد.
برای این کار از متد reset_mock() استفاده می‌کنیم.
۱. پاک‌کردن تاریخچه فراخوانی‌ها
from unittest.mock import Mock

api = Mock()

api("Hello")
api("Python")

print(api.call_count)

api.reset_mock()

print(api.call_count)

خروجی:
2
0

با اجرای reset_mock() اطلاعات مربوط به فراخوانی‌های قبلی پاک می‌شوند.
۲. بررسی تاریخچه پس از Reset
from unittest.mock import Mock

api = Mock()

api(10)
api(20)

api.reset_mock()

print(api.call_args)
print(api.call_args_list)
print(api.call_count)

خروجی:
None
[]
0

۳. آیا reset_mock() مقدار return_value را هم پاک می‌کند؟
به‌صورت پیش‌فرض خیر.
from unittest.mock import Mock

api = Mock(return_value="Success")

api()
api.reset_mock()

print(api())

خروجی:
Success

اگر بخواهیم مقدار بازگشتی نیز بازنشانی شود، باید از پارامتر return_value=True استفاده کنیم:
api.reset_mock(return_value=True)

همچنین با side_effect=True می‌توان رفتار تعیین‌شده در side_effect را پاک کرد:
api.reset_mock(side_effect=True)

۴. بازنشانی Mockهای فرزند
اگر Mock دارای Mockهای فرزند باشد، reset_mock() به‌صورت پیش‌فرض تاریخچه آن‌ها را نیز بازنشانی می‌کند.
from unittest.mock import Mock

service = Mock()

service.database.save()
service.database.delete()

service.reset_mock()

print(service.mock_calls)

خروجی:
[]

نکات مهم
reset_mock() تاریخچه فراخوانی‌ها را پاک می‌کند.
به‌صورت پیش‌فرض return_value و side_effect حفظ می‌شوند.
با return_value=True مقدار بازگشتی بازنشانی می‌شود.
با side_effect=True رفتار جانبی پاک می‌شود.
این متد معمولاً برای استفاده مجدد از Mock در چند مرحله تست کاربرد دارد.
🎯 تمرین
یک Mock با return_value=100 بسازید.
آن را سه بار فراخوانی کنید، سپس با reset_mock() تاریخچه را پاک کنید و بررسی کنید:
مقدار call_count صفر شده باشد.
تاریخچه call_args_list خالی باشد.
مقدار return_value همچنان ۱۰۰ باقی مانده باشد.
پست ۲۶۴ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۶۵
استفاده از PropertyMock برای تست Propertyها
در پایتون می‌توانیم با استفاده از @property، متدها را مانند ویژگی‌های معمولی فراخوانی کنیم.
اما هنگام تست چنین ویژگی‌هایی، گاهی لازم است مقدار آن‌ها را بدون اجرای منطق اصلی کنترل کنیم.
برای این کار از PropertyMock در کتابخانه unittest.mock استفاده می‌کنیم.
۱. یک کلاس با Property
class Product:
@property
def price(self):
return 500

استفاده از این ویژگی:
product = Product()

print(product.price)

خروجی:
500

۲. جایگزینی Property با PropertyMock
from unittest.mock import PropertyMock, patch

class Product:
@property
def price(self):
return 500


product = Product()

with patch.object(
Product,
"price",
new_callable=PropertyMock
) as mock_price:

mock_price.return_value = 1000

print(product.price)

خروجی:
1000

در این مثال، مقدار Property به‌صورت موقت تغییر کرده است؛ بدون اینکه کد اصلی آن اجرا شود.
۳. بررسی تعداد دسترسی به Property
با PropertyMock می‌توانیم بررسی کنیم که یک ویژگی چند بار خوانده شده است:
from unittest.mock import PropertyMock, patch

class User:
@property
def name(self):
return "Ali"


with patch.object(
User,
"name",
new_callable=PropertyMock
) as mock_name:

mock_name.return_value = "Test"

user = User()

print(user.name)
print(user.name)

mock_name.assert_called_with()
print(mock_name.call_count)

خروجی:
Test
Test
2

نکته مهم
PropertyMock باید روی کلاس یا شیءای قرار بگیرد که Property از آن تعریف شده است. در مثال بالا، آن را روی کلاس User قرار دادیم.
همچنین برای تغییر مقدار Property از mock_name.return_value و برای بررسی مقداردهی آن می‌توان از mock_name.assert_called_once_with(value) استفاده کرد.
🎯 تمرین
کلاسی به نام Account بسازید که یک Property به نام balance داشته باشد و مقدار اصلی آن 2000 باشد.
سپس با PropertyMock مقدار آن را در تست به 5000 تغییر دهید و بررسی کنید که مقدار اصلی پس از پایان Patch دوباره قابل‌دسترسی باشد.
پست ۲۶۵ از دوره جامع Python 🐍
👍1
🇮🇷 ۱۳ آبان | روز دانش‌آموز و روز مبارزه با استکبار جهانی

۱۳ آبان، یادآور روزهایی است که دانش‌آموزان و دانشجویان ایرانی برای استقلال، آزادی و آینده کشورشان ایستادگی کردند.

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

ما باور داریم که قدرت واقعی یک ملت، در دانش، آگاهی و توانایی ساختن آن نهفته است.

بیایید با یادگیری، خلاقیت و پیشرفت علمی، سهمی در ساختن آینده داشته باشیم.

🌱 آینده را کسانی می‌سازند که امروز برای یادگرفتن تلاش می‌کنند.

🇮🇷 روز دانش‌آموز گرامی باد.
👍1👎1