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 — پست ۲۵۰
تست‌کردن فایل‌ها و پوشه‌ها با tempfile
در پروژه‌های واقعی، توابع زیادی با فایل‌ها و پوشه‌ها کار می‌کنند؛ مثلاً خواندن تنظیمات، ذخیره گزارش یا پردازش فایل‌های ورودی.
اما اگر تست‌ها مستقیماً فایل‌های واقعی پروژه را تغییر دهند، ممکن است اطلاعات مهمی از بین برود یا نتیجه تست‌ها به وضعیت سیستم وابسته شود.
ماژول استاندارد tempfile امکان ایجاد فایل‌ها و پوشه‌های موقت را فراهم می‌کند تا تست‌ها محیط مستقل‌تری داشته باشند.
۱. ایجاد فایل موقت با TemporaryFile
import tempfile

with tempfile.TemporaryFile(
mode="w+t",
encoding="utf-8"
) as file:
file.write("Hello Python")
file.seek(0)

content = file.read()
print(content)

خروجی:
Hello Python

در این مثال:
w+t فایل را برای خواندن و نوشتن متن باز می‌کند.
write() متن را می‌نویسد.
seek(0) موقعیت خواندن را به ابتدای فایل برمی‌گرداند.
read() محتوای فایل را می‌خواند.
با پایان بلوک with، فایل بسته می‌شود و فایل موقت معمولاً به‌صورت خودکار حذف می‌شود.
رفتار دقیق حذف فایل موقت به نوع تابع و سیستم‌عامل وابسته است.
۲. تست یک تابع که از مسیر فایل استفاده می‌کند
فرض کنید تابع زیر در فایل config_reader.py قرار دارد:
from pathlib import Path


def read_config(path):
return Path(path).read_text(encoding="utf-8")

می‌خواهیم بررسی کنیم که تابع محتوای فایل را درست می‌خواند؛ بدون اینکه به یک فایل تنظیمات واقعی وابسته باشیم.
نوشتن تست
import unittest
import tempfile
from pathlib import Path

from config_reader import read_config


class TestConfigReader(unittest.TestCase):

def test_read_config(self):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "config.txt"
path.write_text(
"theme=dark",
encoding="utf-8"
)

result = read_config(path)

self.assertEqual(result, "theme=dark")


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

🔍 چرا این روش مناسب است؟
۱. TemporaryDirectory() یک پوشه موقت ایجاد می‌کند.
۲. فایل config.txt داخل همان پوشه ساخته می‌شود.
۳. تابع واقعی read_config() روی مسیر فایل اجرا می‌شود.
۴. با پایان بلوک with، پوشه موقت و محتوای آن برای پاک‌سازی علامت‌گذاری و حذف می‌شوند.
در این تست از patch() استفاده نکردیم؛ چون هدف ما بررسی رفتار واقعی خواندن فایل است، نه شبیه‌سازی آن.
۳. تفاوت ابزارهای مهم tempfile
ابزار
کاربرد
TemporaryFile()
ایجاد فایل موقت برای کار مستقیم با محتوای آن
NamedTemporaryFile()
ایجاد فایل موقت با نام و مسیر قابل‌دسترسی
TemporaryDirectory()
ایجاد پوشه موقت برای نگهداری یک یا چند فایل
SpooledTemporaryFile()
نگهداری داده در حافظه تا رسیدن به آستانه مشخص و سپس انتقال به فایل موقت
نکته: اگر تابع مورد آزمایش به مسیر واقعی فایل نیاز دارد، TemporaryDirectory() یا NamedTemporaryFile() معمولاً انتخاب مناسب‌تری از TemporaryFile() است؛ زیرا TemporaryFile() الزاماً مسیر قابل‌استفاده‌ای در اختیار برنامه قرار نمی‌دهد.
۴. یک اشتباه رایج
اگر تابع شما مسیر نسبی می‌گیرد، نباید فرض کنید فایل همیشه نسبت به پوشه پروژه پیدا می‌شود. مسیر نسبی معمولاً نسبت به پوشه کاری فعلی فرایند تفسیر می‌شود.
استفاده از مسیر صریح ساخته‌شده با Path در تست، وابستگی به پوشه کاری را کاهش می‌دهد.
🎯 تمرین
تابعی به نام count_lines(path) بنویس که تعداد خطوط یک فایل متنی را برگرداند.
سپس با TemporaryDirectory() فایلی بساز که شامل سه خط باشد و تستی بنویس که نتیجه تابع برابر 3 باشد.
پست ۲۵۰ از دوره جامع Python 🐍
1👍1
🐍 آموزش جامع Python — پست ۲۵۱
تست‌کردن کدهای ناهمگام با unittest.IsolatedAsyncioTestCase
در پایتون، توابعی که با async def تعریف می‌شوند، توابع ناهمگام (Asynchronous) هستند. برای تست این توابع باید اجرای آن‌ها را با await مدیریت کنیم.
کتابخانه استاندارد unittest کلاسی به نام IsolatedAsyncioTestCase دارد که برای تست کدهای ناهمگام طراحی شده است.
🔹 ۱. یک تابع ناهمگام بسازیم
import asyncio


async def get_user_name():
await asyncio.sleep(0.1)
return "Sara"

فراخوانی این تابع یک شیء coroutine ایجاد می‌کند. برای دریافت نتیجه، باید آن را اجرا و منتظر تکمیلش بمانیم.
🔹 ۲. نوشتن تست ناهمگام
import unittest
import asyncio


async def get_user_name():
await asyncio.sleep(0.1)
return "Sara"


class TestUser(unittest.IsolatedAsyncioTestCase):

async def test_get_user_name(self):
result = await get_user_name()

self.assertEqual(result, "Sara")


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

🔍 نکات مهم
متد تست ناهمگام باید با async def تعریف شود.
درون متد تست، برای انتظار نتیجه تابع ناهمگام از await استفاده می‌کنیم.
IsolatedAsyncioTestCase محیط اجرای ناهمگام مستقلی برای تست‌ها فراهم می‌کند.
در این مثال، asyncio.sleep() صرفاً برای شبیه‌سازی یک عملیات زمان‌بر است.
🔹 ۳. بررسی خطا در تابع ناهمگام
می‌توانیم خطاهای مورد انتظار را نیز آزمایش کنیم:
async def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")

return a / b

تست:
async def test_divide_by_zero(self):
with self.assertRaises(ValueError):
await divide(10, 0)

این متد تست باید داخل کلاس TestUser یا کلاس دیگری از نوع IsolatedAsyncioTestCase قرار بگیرد.
نکته: اگر از assertRaises() استفاده می‌کنیم، باید فراخوانی coroutine را با await داخل بلوک آن قرار دهیم تا خطای زمان اجرای تابع واقعاً بررسی شود.
🎯 تمرین
تابع ناهمگام زیر را پیاده‌سازی و آزمایش کن:
async def get_square(number)
این تابع باید مربع عدد را برگرداند. سپس دو تست بنویس:
۱. بررسی کن که get_square(5) نتیجه 25 را برمی‌گرداند.
۲. اگر تصمیم گرفتی ورودی منفی مجاز نباشد، برای آن ValueError تعریف کن و تست مناسبی بنویس.
اجرای تست‌ها:
python -m unittest -v

پست ۲۵۱ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۲۵۲
شبیه‌سازی توابع ناهمگام با AsyncMock
در پست قبل یاد گرفتیم توابع async را با IsolatedAsyncioTestCase آزمایش کنیم. حالا می‌خواهیم وابستگی‌های ناهمگام را بدون اجرای عملیات واقعی شبیه‌سازی کنیم.
ابزار AsyncMock از ماژول unittest.mock برای همین کار طراحی شده است.
🔹 ۱. چرا به AsyncMock نیاز داریم؟
فرض کنید تابعی برای دریافت اطلاعات کاربر از یک API داریم. هنگام تست نمی‌خواهیم درخواست واقعی اینترنتی ارسال کنیم؛ فقط می‌خواهیم رفتار تابع خودمان را بررسی کنیم.
فایل user_service.py:
async def fetch_user(api):
return await api.get_user(10)

این تابع به یک متد ناهمگام به نام get_user() وابسته است.
🔹 ۲. نوشتن تست با AsyncMock
import unittest
from unittest.mock import AsyncMock


async def fetch_user(api):
return await api.get_user(10)


class TestUserService(unittest.IsolatedAsyncioTestCase):

async def test_fetch_user(self):
api = unittest.mock.Mock()
api.get_user = AsyncMock(
return_value={"id": 10, "name": "Sara"}
)

result = await fetch_user(api)

self.assertEqual(
result,
{"id": 10, "name": "Sara"}
)

api.get_user.assert_awaited_once_with(10)


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

🔍 توضیح کد
AsyncMock یک وابستگی ناهمگام ساختگی ایجاد می‌کند.
return_value مشخص می‌کند که انتظار داریم نتیجه await api.get_user(10) چه باشد.
assert_awaited_once_with(10) بررسی می‌کند که متد دقیقاً یک بار با آرگومان 10 مورد await قرار گرفته باشد.
در این تست، هیچ درخواست واقعی به API ارسال نمی‌شود.
🔸 تفاوت assert_called_once_with() و assert_awaited_once_with()
متد
چه چیزی را بررسی می‌کند؟
assert_called_once_with()
تابع یک بار با آرگومان‌های مشخص فراخوانی شده است
assert_awaited_once_with()
فراخوانی ناهمگام یک بار با آرگومان‌های مشخص مورد await قرار گرفته است
برای بررسی رفتار ناهمگام، معمولاً assert_awaited_once_with() دقیق‌تر است.
⚠️ نکته مهم
در کد بالا از unittest.mock.Mock() استفاده کرده‌ایم؛ بنابراین باید import unittest نیز وجود داشته باشد. اگر فقط AsyncMock را وارد کرده باشید، می‌توانید به‌جای آن مستقیماً بنویسید:
from unittest.mock import AsyncMock, Mock

api = Mock()
api.get_user = AsyncMock(
return_value={"id": 10, "name": "Sara"}
)

🎯 تمرین
تابعی به نام fetch_product(api) بنویس که محصول شماره 5 را با await api.get_product(5) دریافت کند.
سپس با AsyncMock تستی بنویس که:
۱. نتیجه ساختگی {"id": 5, "name": "Keyboard"} باشد.
۲. نتیجه تابع را بررسی کند.
۳. تأیید کند get_product دقیقاً یک بار با آرگومان 5 مورد await قرار گرفته است.
پست ۲۵۲ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۲۵۳
تست‌کردن فراخوانی‌ها با call_args و call_args_list
در تست‌نویسی، گاهی فقط دانستن نتیجه یک تابع کافی نیست؛ باید بررسی کنیم تابع‌های وابسته با چه آرگومان‌هایی و چند بار فراخوانی شده‌اند.
در unittest.mock ابزارهایی مانند call_args و call_args_list این امکان را فراهم می‌کنند.
🔹 ۱. بررسی آخرین فراخوانی با call_args
from unittest.mock import Mock

send_message = Mock()

send_message("Sara", "Hello")
send_message("Ali", "Welcome")

print(send_message.call_args)

خروجی:
call('Ali', 'Welcome')

ویژگی call_args اطلاعات آخرین فراخوانی را نگه می‌دارد؛ نه تمام فراخوانی‌ها را.
🔹 ۲. بررسی تمام فراخوانی‌ها با call_args_list
from unittest.mock import Mock

send_message = Mock()

send_message("Sara", "Hello")
send_message("Ali", "Welcome")

print(send_message.call_args_list)

خروجی:
[call('Sara', 'Hello'), call('Ali', 'Welcome')]

این ویژگی تمام فراخوانی‌ها را به ترتیب وقوع نگه می‌دارد.
🔹 ۳. بررسی آرگومان‌ها
هر فراخوانی ثبت‌شده شامل دو بخش است:
args: آرگومان‌های موقعیتی (Positional Arguments)
kwargs: آرگومان‌های نام‌دار (Keyword Arguments)
مثال:
from unittest.mock import Mock

save_user = Mock()

save_user(10, name="Sara", active=True)

args, kwargs = save_user.call_args

print(args)
print(kwargs)

خروجی:
(10,)
{'name': 'Sara', 'active': True}

وجود ویرگول در (10,) نشان می‌دهد که این یک تاپل تک‌عضوی است.
🔹 ۴. استفاده در تست واقعی
فرض کنیم تابع زیر را داریم:
def notify_users(send_message, users):
for user in users:
send_message(user, "New update")

تست آن:
import unittest
from unittest.mock import Mock, call


class TestNotifications(unittest.TestCase):

def test_notify_users(self):
send_message = Mock()

notify_users(
send_message,
["Sara", "Ali"]
)

self.assertEqual(
send_message.call_args_list,
[
call("Sara", "New update"),
call("Ali", "New update"),
],
)


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

در این تست، علاوه بر تعداد فراخوانی‌ها، ترتیب و آرگومان‌های هر فراخوانی نیز بررسی می‌شود.
⚠️ نکته مهم
اگر فقط می‌خواهید تعداد فراخوانی‌ها را بررسی کنید، از call_count استفاده کنید. اگر آخرین آرگومان‌ها مهم‌اند، call_args مناسب است؛ و اگر تمام فراخوانی‌ها اهمیت دارند، call_args_list انتخاب بهتری است.
همچنین assert_has_calls() برای بررسی وجود یک توالی از فراخوانی‌ها کاربرد دارد؛ به‌طور پیش‌فرض فراخوانی‌های اضافی قبل یا بعد از آن توالی مجازند، اما ترتیب فراخوانی‌های مورد انتظار باید حفظ شود.
🎯 تمرین
تابعی به نام log_actions(logger, actions) بنویس که برای هر عضو فهرست، تابع logger را با آن عضو فراخوانی کند.
سپس با Mock بررسی کن:
۱. تعداد فراخوانی‌ها برابر تعداد اعضای فهرست باشد.
۲. آرگومان‌های هر فراخوانی درست باشند.
۳. ترتیب فراخوانی‌ها مطابق ترتیب فهرست ورودی باشد.
پست ۲۵۳ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۲۵۴
تست‌کردن فراخوانی‌های زنجیره‌ای با mock_calls
در پست قبل با call_args و call_args_list آشنا شدیم. حالا می‌خواهیم حالتی را بررسی کنیم که یک شیء ساختگی، متدهای تو‌در‌تو یا زنجیره‌ای دارد.
برای بررسی تمام این فراخوانی‌ها می‌توانیم از ویژگی mock_calls استفاده کنیم.
🔹 ۱. یک مثال ساده
from unittest.mock import Mock

client = Mock()

client.session.get("/users")
client.session.close()

print(client.mock_calls)

خروجی:
[call.session.get('/users'), call.session.close()]

ویژگی mock_calls فراخوانی‌های ثبت‌شده روی خود Mock و متدها و ویژگی‌های ساختگی وابسته به آن را به ترتیب وقوع نگه می‌دارد.
🔹 ۲. تفاوت call_args_list و mock_calls
from unittest.mock import Mock

client = Mock()

client.get("/users")
client.session.close()

print(client.call_args_list)
print(client.mock_calls)

خروجی:
[call('/users')]
[call.get('/users'), call.session.close()]

در این مثال:
call_args_list فقط فراخوانی‌های مستقیم خود client را ثبت می‌کند.
mock_calls فراخوانی‌های متدها و اشیای ساختگی وابسته را نیز ثبت می‌کند.
🔹 ۳. استفاده در تست
فرض کنیم تابعی داریم که اطلاعات را دریافت و سپس اتصال را می‌بندد:
def load_users(client):
client.session.get("/users")
client.session.close()

تست:
import unittest
from unittest.mock import Mock, call


class TestUserClient(unittest.TestCase):

def test_load_users(self):
client = Mock()

load_users(client)

self.assertEqual(
client.mock_calls,
[
call.session.get("/users"),
call.session.close(),
],
)


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

این تست بررسی می‌کند که هر دو فراخوانی با آرگومان‌های درست و به ترتیب مورد انتظار اتفاق افتاده‌اند.
⚠️ نکته مهم
mock_calls ترتیب فراخوانی‌ها را نشان می‌دهد؛ اما همیشه برای اثبات ترتیب اجرای تمام عملیات داخلی کافی نیست. به‌خصوص، آرگومان‌های یک فراخوانی والد در اطلاعات فراخوانی‌های تو‌در‌توی آن تکرار نمی‌شوند. اگر بررسی دقیق آرگومان‌های والد ضروری است، آن‌ها را جداگانه آزمایش کنید.
همچنین استفاده از Mock نباید جای تست رفتار واقعی سیستم را بگیرد؛ Mock برای بررسی تعامل با وابستگی‌ها مناسب است.
🎯 تمرین
تابعی به نام process_order(api) بنویس که به‌ترتیب:
۱. api.orders.create() را با مقدار "ORD-101" فراخوانی کند.
۲. api.orders.confirm() را فراخوانی کند.
سپس با Mock و mock_calls بررسی کن که این دو عملیات به ترتیب درست انجام شده‌اند.
پست ۲۵۴ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۲۵۵
تفاوت Mock، MagicMock و NonCallableMock
در تست‌نویسی پایتون، همه اشیای ساختگی رفتار یکسانی ندارند. انتخاب نوع مناسب Mock باعث می‌شود تست‌ها دقیق‌تر باشند و رفتار واقعی برنامه را بهتر شبیه‌سازی کنند.
سه ابزار مهم در unittest.mock عبارت‌اند از:
Mock
MagicMock
NonCallableMock
🔹 ۱. کلاس Mock
Mock برای شبیه‌سازی اشیا و بررسی تعامل با آن‌ها استفاده می‌شود.
from unittest.mock import Mock

user = Mock()

user.name = "Sara"
user.get_age.return_value = 25

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

خروجی:
Sara
25

با return_value مشخص کردیم که فراخوانی get_age() چه نتیجه‌ای داشته باشد.
نکته: ویژگی‌های ساختگی مانند get_age معمولاً هنگام دسترسی ایجاد می‌شوند، مگر اینکه محدودیت‌هایی مانند spec اعمال شده باشد.
🔹 ۲. کلاس MagicMock
MagicMock مانند Mock است، اما تعداد زیادی از متدهای ویژه پایتون (Magic Methods) را نیز پشتیبانی می‌کند.
برای مثال، می‌توانیم رفتار len() و عملگر in را شبیه‌سازی کنیم:
from unittest.mock import MagicMock

items = MagicMock()

items.__len__.return_value = 3
items.__contains__.return_value = True

print(len(items))
print("Python" in items)

خروجی:
3
True

این قابلیت برای تست کدهایی مفید است که با عملیات ویژه پایتون کار می‌کنند.
البته نتیجه‌های بالا ساختگی‌اند؛ این کد یک فهرست واقعی با سه عضو ایجاد نمی‌کند.
🔹 ۳. کلاس NonCallableMock
گاهی می‌خواهیم یک شیء ساختگی داشته باشیم که قابل فراخوانی نباشد؛ یعنی نتوان آن را مانند تابع صدا زد.
from unittest.mock import NonCallableMock

config = NonCallableMock()
config.mode = "production"

print(config.mode)

خروجی:
production

اما این دستور خطا ایجاد می‌کند:
config()

زیرا NonCallableMock برای فراخوانی مانند تابع طراحی نشده است.
🔍 مقایسه سریع
ویژگی
Mock
MagicMock
NonCallableMock
ساخت ویژگی‌های ساختگی
بله
بله
بله
پشتیبانی آماده از بسیاری از متدهای ویژه
محدودتر
بله
محدودتر
قابل فراخوانی مانند تابع
بله
بله
خیر
کاربرد اصلی
شبیه‌سازی وابستگی‌ها
شبیه‌سازی اشیای دارای رفتارهای ویژه
شبیه‌سازی اشیای غیرقابل‌فراخوانی
⚠️ نکته مهم: محدودکردن رفتار ساختگی
اگر بخواهیم Mock فقط ویژگی‌های موجود در یک شیء واقعی را بپذیرد، می‌توانیم از spec استفاده کنیم:
from unittest.mock import Mock


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


user = Mock(spec=User)

print(user.get_name())

اما دسترسی به ویژگی تعریف‌نشده‌ای مانند user.get_email() خطا ایجاد می‌کند.
spec نام ویژگی‌ها را محدود می‌کند، ولی لزوماً تمام رفتارها و امضاهای شیء واقعی را اعتبارسنجی نمی‌کند. برای محدودیت سخت‌گیرانه‌تر، spec_set و برای بررسی امضای فراخوانی‌ها، autospec نیز وجود دارند که در ادامه بررسی خواهیم کرد.
🎯 تمرین
یک کلاس ShoppingCart با متد add_item() و متد ویژه __len__() در نظر بگیر.
۱. با Mock فراخوانی add_item() را شبیه‌سازی کن.
۲. با MagicMock مقدار بازگشتی len(cart) را برابر 2 قرار بده.
۳. با NonCallableMock یک تنظیمات ساختگی بساز و بررسی کن که فراخوانی آن مجاز نیست.
پست ۲۵۵ از دوره جامع Python 🐍
👍1
🏛 ۷ آبان | روز بزرگداشت کوروش بزرگ

🇮🇷 کوروش بزرگ؛ نمادی از قدرت، تدبیر و احترام به انسان

کوروش بزرگ، بنیان‌گذار شاهنشاهی هخامنشی، یکی از تأثیرگذارترین فرمانروایان تاریخ ایران باستان بود. او با گسترش قلمرو ایران و ایجاد ساختاری گسترده برای اداره سرزمین‌های گوناگون، نقشی مهم در تاریخ جهان ایفا کرد.

📜 استوانه کوروش که پس از فتح بابل در سال ۵۳۹ پیش از میلاد تهیه شد، از مهم‌ترین آثار تاریخی مرتبط با دوران اوست و اطلاعاتی درباره سیاست‌های حکومتی و بازسازی معابد در بابل ارائه می‌دهد.

✨ یادآوری تاریخ کوروش، فرصتی است برای شناخت بهتر تمدن ایران باستان، میراث فرهنگی و نقش ایران در تاریخ جهان.

تاریخ را بشناسیم، میراثمان را پاس بداریم و آینده را آگاهانه بسازیم. 🇮🇷

📌 نکته: ۷ آبان روز بزرگداشت کوروش است، اما تعطیل رسمی نیست.
1👍1
💻🌍 ۲۹ اکتبر | روز جهانی کدنویسی

🎉 روز جهانی کدنویسی مبارک!

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

🚀 برای برنامه‌نویس‌ها، هر خط کد فرصتی برای یادگیری و ساختن است.

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

🧠 کمتر فقط مصرف‌کننده فناوری باشیم؛ بیشتر آن را بسازیم!

روز جهانی کدنویسی مبارک؛ آینده را با کد می‌سازیم. 💻
👍2
📚 معرفی کتاب آموزش جامع پایتون 🐍

سلام به همه برنامه‌نویس‌ها! 👋

یک کتاب آموزش پایتون پیدا کردم و گفتم اون رو با شما هم به اشتراک بگذارم تا اگر دوست داشتید، بتونید تهیه‌اش کنید و برای یادگیری بیشتر ازش استفاده کنید.

📖 نسخه الکترونیکی: 🔗 دریافت از طاقچه: https://taaghche.com/book/251603

📦 نسخه فیزیکی: می‌تونید کتاب رو به‌صورت چاپی هم از دیجی‌کالا تهیه کنید.

💙 من هم سعی می‌کنم نسخه PDF کتاب رو خریداری کنم و در اختیار شما قرار بدم تا همه بتونیم از این منبع آموزشی استفاده کنیم.

💻 یادگیری رو ادامه بدید؛ هر قدم کوچیک، شما رو به یک برنامه‌نویس بهتر تبدیل می‌کنه!
👍1
🐍 راهنمای پرسش و رفع اشکال دوره Python
دوستان، اگر در طول دوره در هر بخش از آموزش، کدها، تمرین‌ها یا اجرای برنامه به مشکلی برخوردید، می‌توانید سؤال خودتان را مطرح کنید تا بررسی شود.
💬 چه زمانی پیام بدهیم؟
اگر در مواردی مثل این‌ها مشکل داشتید:
🔹 مفهوم یک مبحث را متوجه نشدید
🔹 کدی که نوشته‌اید اجرا نمی‌شود
🔹 با یک Error / Exception مواجه شدید
🔹 خروجی برنامه با چیزی که انتظار داشتید متفاوت است
🔹 در انجام تمرین‌های دوره مشکل دارید
🔹 نمی‌دانید یک بخش از کد چگونه کار می‌کند
🔹 در نصب Python یا اجرای برنامه‌ها مشکلی دارید
🔹 درباره یک بخش از آموزش نیاز به توضیح بیشتری دارید
🔹 در کد خودتان با یک رفتار غیرمنتظره مواجه شدید
می‌توانید مشکل را مطرح کنید.
📌 برای بررسی سریع‌تر مشکل
لطفاً هنگام ارسال سؤال، تا حد امکان این موارد را هم بفرستید:
1️⃣ کدی که نوشته‌اید
# کد خودتان را اینجا قرار دهید

2️⃣ خطایی که دریافت می‌کنید
اگر Error نمایش داده می‌شود، متن کامل آن را ارسال کنید.
3️⃣ توضیح کوتاه درباره مشکل
مثلاً:
انتظار داشتم برنامه عدد 20 را چاپ کند، اما 0 چاپ می‌شود.

این اطلاعات کمک می‌کند مشکل دقیق‌تر بررسی شود.
🕐 ساعت پاسخ‌گویی
برای پاسخ‌گویی و رفع اشکال، هر روز در بازه:
🌙 ساعت ۱۱ تا ۱۲ شب
در خدمت شما هستیم.
اگر خارج از این ساعت پیام ارسال کردید، ممکن است پاسخ‌گویی با تأخیر انجام شود؛ اما پیام شما بررسی خواهد شد.
🆘 راه ارتباطی
اگر در کد، تمرین، آموزش یا اجرای برنامه به مشکل خوردید، از طریق آیدی زیر پیام دهید:
👉 @JarvisAI_77
لطفاً سؤال خودتان را تا حد امکان واضح و همراه با کد و متن خطا ارسال کنید تا بتوانیم دقیق‌تر راهنمایی کنیم.
📚 هدف این است که هیچ مشکلی در مسیر یادگیری Python باعث متوقف شدن ادامه مسیرتان نشود.
سؤال بپرسید، مشکل را پیدا کنید و با هم حلش کنیم. 🐍
👍2
🐍 آموزش جامع 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