🐍 آموزش جامع Python — پست ۲۴۶
مدیریت منابع تست با
در پست قبل با
این متد برای ثبت عملیات پاکسازی استفاده میشود؛ عملیاتی که پس از پایان تست باید اجرا شوند، مانند بستن فایل یا آزادسازی یک منبع.
🔹 مثال عملی
فرض کنید تست ما یک فایل موقت باز میکند. میخواهیم مطمئن شویم فایل پس از پایان تست بسته میشود.
🔍 کد چگونه کار میکند؟
پس از پایان هر تست، تابع ثبتشده اجرا میشود؛ حتی اگر تست با شکست مواجه شود یا اجرای آن با خطا متوقف شود.
از آنجا که
🔸 تفاوت
ویژگی
روش استفاده
بازنویسی یک متد
ثبت یک یا چند تابع
تعداد عملیات پاکسازی
میتوان چند عملیات را در آن نوشت
میتوان چند تابع جداگانه ثبت کرد
ترتیب اجرا
پس از تست، در صورت موفقیت
توابع ثبتشده با ترتیب معکوس ثبت اجرا میشوند
خطا در
ممکن است اجرا نشود
توابعی که پیش از خطا ثبت شدهاند، اجرا میشوند
نکته مهم: توابع
🎯 تمرین
کلاسی برای تست یک منبع فرضی بساز که:
۱. در
۲. با
۳. در تابع پاکسازی، یک پیام چاپ کند.
۴. بررسی کند که دادهها در زمان اجرای تست در دسترس هستند.
هدف این است که تفاوت آمادهسازی تست و پاکسازی خودکار منابع را در عمل درک کنید.
پست ۲۴۶ از دوره جامع Python 🐍
مدیریت منابع تست با
addCleanup()در پست قبل با
setUp() و tearDown() آشنا شدیم. حالا میخواهیم روش دیگری در unittest یاد بگیریم: متد addCleanup().این متد برای ثبت عملیات پاکسازی استفاده میشود؛ عملیاتی که پس از پایان تست باید اجرا شوند، مانند بستن فایل یا آزادسازی یک منبع.
🔹 مثال عملی
فرض کنید تست ما یک فایل موقت باز میکند. میخواهیم مطمئن شویم فایل پس از پایان تست بسته میشود.
import unittest
class TestFileHandling(unittest.TestCase):
def setUp(self):
self.file = open("test.txt", "w", encoding="utf-8")
self.addCleanup(self.file.close)
def test_file_is_open(self):
self.assertFalse(self.file.closed)
def test_write_to_file(self):
self.file.write("Hello Python")
if __name__ == "__main__":
unittest.main()
🔍 کد چگونه کار میکند؟
open() فایل را باز میکند.self.addCleanup(self.file.close) تابع بستن فایل را برای مرحله پاکسازی ثبت میکند.پس از پایان هر تست، تابع ثبتشده اجرا میشود؛ حتی اگر تست با شکست مواجه شود یا اجرای آن با خطا متوقف شود.
از آنجا که
setUp() برای هر تست اجرا میشود، هر تست فایل خودش را باز میکند و عملیات پاکسازی مربوط به همان فایل را ثبت میکند.🔸 تفاوت
addCleanup() با tearDown()ویژگی
tearDown()addCleanup()روش استفاده
بازنویسی یک متد
ثبت یک یا چند تابع
تعداد عملیات پاکسازی
میتوان چند عملیات را در آن نوشت
میتوان چند تابع جداگانه ثبت کرد
ترتیب اجرا
پس از تست، در صورت موفقیت
setUp()توابع ثبتشده با ترتیب معکوس ثبت اجرا میشوند
خطا در
setUp()ممکن است اجرا نشود
توابعی که پیش از خطا ثبت شدهاند، اجرا میشوند
نکته مهم: توابع
addCleanup() حتی اگر متد تست شکست بخورد یا tearDown() با خطا مواجه شود، اجرا میشوند. اگر setUp() قبل از ثبت عملیات پاکسازی با خطا مواجه شود، طبیعتاً پاکسازی ثبتنشدهای وجود ندارد.🎯 تمرین
کلاسی برای تست یک منبع فرضی بساز که:
۱. در
setUp() یک فهرست ایجاد کند.۲. با
addCleanup() تابعی را برای پاکسازی ثبت کند.۳. در تابع پاکسازی، یک پیام چاپ کند.
۴. بررسی کند که دادهها در زمان اجرای تست در دسترس هستند.
هدف این است که تفاوت آمادهسازی تست و پاکسازی خودکار منابع را در عمل درک کنید.
پست ۲۴۶ از دوره جامع Python 🐍
👍1
🐍 آموزش جامع Python — پست ۲۴۷
مدیریت منابع در تستها با
در تستنویسی، بعضی منابع برای هر تست بهصورت جداگانه آماده میشوند؛ اما بعضی دیگر، مانند یک منبع مشترک یا دادههای پرهزینه برای آمادهسازی، بهتر است فقط یک بار برای کل کلاس ایجاد شوند.
در
۱. ساختار اصلی
در این مثال،
۲. چرا از
این دو متد بهجای یک نمونه از کلاس تست، خود کلاس را دریافت میکنند؛ بنابراین پارامتر آنها
اکنون داده از طریق کلاس قابل دسترسی است:
۳. تفاوت با
متد
زمان اجرا
پیش از هر تست
پس از هر تست، اگر
یک بار پیش از تستهای کلاس
پس از پایان اجرای تستهای کلاس
برای مثال، اگر کلاس سه متد تست داشته باشد، در اجرای معمول هر چهار متد مربوط به چرخه عمر به این صورت رفتار میکنند:
البته اگر راهاندازی با خطا مواجه شود یا اجرای تستها به شکل غیرمعمول متوقف شود، این ترتیب ممکن است کامل نشود.
۴. مثال کاربردی با دادههای مشترک
در اینجا دیکشنری یک بار ساخته میشود و همه تستها آن را میخوانند.
نکته مهم: دادههای مشترک ممکن است بین تستها تغییر کنند. اگر یک تست دیکشنری را تغییر دهد، تستهای دیگر نیز میتوانند تغییر را مشاهده کنند. بنابراین، برای دادههای قابلتغییر یا تستهای مستقل، معمولاً ساخت داده در
همچنین
🎯 تمرین
کلاسی به نام
۱. در
۲. در تست اول، قیمت یکی از محصولات را بررسی کند.
۳. در تست دوم، تعداد محصولات را بررسی کند.
۴. در
۵. توضیح بده چرا نباید بدون دلیل، دادههای مشترک را در هر تست تغییر دهیم.
اجرای تستها:
در پست بعدی به سراغ مدیریت خطا هنگام آمادهسازی منابع تست میرویم تا تستها حتی در شرایط شکست نیز پاکسازی قابلاعتمادتری داشته باشند.
پست ۲۴۷ از دوره جامع Python 🐍
مدیریت منابع در تستها با
setUpClass() و tearDownClass()در تستنویسی، بعضی منابع برای هر تست بهصورت جداگانه آماده میشوند؛ اما بعضی دیگر، مانند یک منبع مشترک یا دادههای پرهزینه برای آمادهسازی، بهتر است فقط یک بار برای کل کلاس ایجاد شوند.
در
unittest دو متد ویژه برای این کار داریم:setUpClass() برای آمادهسازی مشترک پیش از اجرای تستهای کلاسtearDownClass() برای پاکسازی مشترک پس از پایان اجرای تستهای کلاس۱. ساختار اصلی
import unittest
class TestExample(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("Class setup")
@classmethod
def tearDownClass(cls):
print("Class cleanup")
def test_first(self):
self.assertEqual(2 + 3, 5)
def test_second(self):
self.assertTrue(10 > 3)
if __name__ == "__main__":
unittest.main()
در این مثال،
setUpClass() یک بار پیش از اجرای تستهای این کلاس اجرا میشود و tearDownClass() پس از پایان اجرای تستهای کلاس اجرا خواهد شد.۲. چرا از
@classmethod استفاده میکنیم؟این دو متد بهجای یک نمونه از کلاس تست، خود کلاس را دریافت میکنند؛ بنابراین پارامتر آنها
cls است.@classmethod
def setUpClass(cls):
cls.shared_data = [10, 20, 30]
اکنون داده از طریق کلاس قابل دسترسی است:
self.assertEqual(sum(self.shared_data), 60)
۳. تفاوت با
setUp() و tearDown()متد
زمان اجرا
setUp()پیش از هر تست
tearDown()پس از هر تست، اگر
setUp() با موفقیت تمام شده باشدsetUpClass()یک بار پیش از تستهای کلاس
tearDownClass()پس از پایان اجرای تستهای کلاس
برای مثال، اگر کلاس سه متد تست داشته باشد، در اجرای معمول هر چهار متد مربوط به چرخه عمر به این صورت رفتار میکنند:
setUpClass() یک بار اجرا میشود.setUp() سه بار اجرا میشود.tearDown() سه بار اجرا میشود.tearDownClass() یک بار اجرا میشود.البته اگر راهاندازی با خطا مواجه شود یا اجرای تستها به شکل غیرمعمول متوقف شود، این ترتیب ممکن است کامل نشود.
۴. مثال کاربردی با دادههای مشترک
import unittest
class TestProducts(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.products = {
"book": 150,
"pen": 20,
"bag": 500,
}
def test_book_price(self):
self.assertEqual(self.products["book"], 150)
def test_pen_price(self):
self.assertEqual(self.products["pen"], 20)
def test_product_count(self):
self.assertEqual(len(self.products), 3)
if __name__ == "__main__":
unittest.main()
در اینجا دیکشنری یک بار ساخته میشود و همه تستها آن را میخوانند.
نکته مهم: دادههای مشترک ممکن است بین تستها تغییر کنند. اگر یک تست دیکشنری را تغییر دهد، تستهای دیگر نیز میتوانند تغییر را مشاهده کنند. بنابراین، برای دادههای قابلتغییر یا تستهای مستقل، معمولاً ساخت داده در
setUp() انتخاب امنتری است.همچنین
tearDownClass() فقط زمانی اجرا میشود که راهاندازی کلاس با موفقیت انجام شده باشد. برای منابعی که باید حتی در صورت شکست بخشی از setUpClass() آزاد شوند، ثبت پاکسازی با cls.addClassCleanup() میتواند مناسبتر باشد.🎯 تمرین
کلاسی به نام
TestInventory بساز که:۱. در
setUpClass() یک دیکشنری مشترک از سه محصول ایجاد کند.۲. در تست اول، قیمت یکی از محصولات را بررسی کند.
۳. در تست دوم، تعداد محصولات را بررسی کند.
۴. در
tearDownClass() پیام پایان تستهای کلاس را چاپ کند.۵. توضیح بده چرا نباید بدون دلیل، دادههای مشترک را در هر تست تغییر دهیم.
اجرای تستها:
python -m unittest -v
در پست بعدی به سراغ مدیریت خطا هنگام آمادهسازی منابع تست میرویم تا تستها حتی در شرایط شکست نیز پاکسازی قابلاعتمادتری داشته باشند.
پست ۲۴۷ از دوره جامع Python 🐍
1👍1
🐍 آموزش جامع Python — پست ۲۴۸
پاکسازی مطمئن منابع با
در پست قبل با
برای این کار،
۱. مشکل
فرض کنید در مرحله آمادهسازی کلاس، یک فایل باز میکنیم و سپس هنگام آمادهسازی منبع دیگری خطایی رخ میدهد.
در این مثال، اگر
۲. راهحل با
بهتر است عملیات پاکسازی را بلافاصله پس از ایجاد منبع ثبت کنیم:
🔍 چه اتفاقی میافتد؟
۱. فایل باز میشود.
۲. تابع
۳. اگر ادامه آمادهسازی کلاس با خطا مواجه شود، عملیات پاکسازی ثبتشده همچنان اجرا میشود.
۴. اگر تستها اجرا شوند، پاکسازی کلاس پس از پایان اجرای تستهای آن انجام میشود.
۳. ترتیب اجرای پاکسازیها
اگر چند عملیات ثبت کنیم:
عملیات پاکسازی به ترتیب معکوس ثبت اجرا میشوند؛ بنابراین ابتدا
این ترتیب برای منابع وابسته به یکدیگر اهمیت دارد. معمولاً باید منبعی که آخر از همه ایجاد شده، زودتر آزاد شود.
۴. تفاوت سه روش
روش
کاربرد
پاکسازی پس از هر تست
پاکسازی مشترک پس از اجرای تستهای کلاس
ثبت عملیات پاکسازی کلاس، حتی اگر
نکته:
🎯 تمرین
کلاسی طراحی کن که در
سپس:
یک تست برای بررسی باز بودن فایل بنویس.
توضیح بده چرا ثبت پاکسازی باید بلافاصله پس از باز کردن فایل انجام شود.
مشخص کن اگر آمادهسازی بعد از باز شدن فایل با خطا مواجه شود، چه اتفاقی میافتد.
پست ۲۴۸ از دوره جامع Python 🐍
پاکسازی مطمئن منابع با
addClassCleanup()در پست قبل با
setUpClass() و tearDownClass() آشنا شدیم. حالا یک مسئله مهم را بررسی میکنیم: اگر آمادهسازی منابع با خطا مواجه شود، چگونه مطمئن شویم منابعی که قبلاً ایجاد شدهاند آزاد میشوند؟برای این کار،
unittest متدی به نام addClassCleanup() در اختیارمان قرار میدهد.۱. مشکل
tearDownClass()فرض کنید در مرحله آمادهسازی کلاس، یک فایل باز میکنیم و سپس هنگام آمادهسازی منبع دیگری خطایی رخ میدهد.
import unittest
class TestResources(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.file = open(
"test.txt",
"w",
encoding="utf-8"
)
raise RuntimeError("Setup failed")
@classmethod
def tearDownClass(cls):
cls.file.close()
در این مثال، اگر
setUpClass() با خطا متوقف شود، نباید به اجرای tearDownClass() برای بستن فایل تکیه کنیم. در نتیجه، فایل ممکن است باز بماند.۲. راهحل با
addClassCleanup()بهتر است عملیات پاکسازی را بلافاصله پس از ایجاد منبع ثبت کنیم:
import unittest
class TestResources(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.file = open(
"test.txt",
"w",
encoding="utf-8"
)
cls.addClassCleanup(cls.file.close)
# ادامه آمادهسازی منابع
# اگر این بخش خطا بدهد، پاکسازی ثبتشده اجرا میشود.
def test_file_is_open(self):
self.assertFalse(self.file.closed)
if __name__ == "__main__":
unittest.main()
🔍 چه اتفاقی میافتد؟
۱. فایل باز میشود.
۲. تابع
close با addClassCleanup() ثبت میشود.۳. اگر ادامه آمادهسازی کلاس با خطا مواجه شود، عملیات پاکسازی ثبتشده همچنان اجرا میشود.
۴. اگر تستها اجرا شوند، پاکسازی کلاس پس از پایان اجرای تستهای آن انجام میشود.
۳. ترتیب اجرای پاکسازیها
اگر چند عملیات ثبت کنیم:
@classmethod
def setUpClass(cls):
cls.addClassCleanup(print, "Cleanup A")
cls.addClassCleanup(print, "Cleanup B")
عملیات پاکسازی به ترتیب معکوس ثبت اجرا میشوند؛ بنابراین ابتدا
Cleanup B و سپس Cleanup A چاپ میشود.این ترتیب برای منابع وابسته به یکدیگر اهمیت دارد. معمولاً باید منبعی که آخر از همه ایجاد شده، زودتر آزاد شود.
۴. تفاوت سه روش
روش
کاربرد
tearDown()پاکسازی پس از هر تست
tearDownClass()پاکسازی مشترک پس از اجرای تستهای کلاس
addClassCleanup()ثبت عملیات پاکسازی کلاس، حتی اگر
setUpClass() شکست بخوردنکته:
addClassCleanup() در نسخههای جدید پایتون در دسترس است؛ برای سازگاری با نسخههای بسیار قدیمی، نسخه پایتون را بررسی کنید.🎯 تمرین
کلاسی طراحی کن که در
setUpClass() یک فایل باز کند و بلافاصله تابع بستن آن را با addClassCleanup() ثبت کند.سپس:
یک تست برای بررسی باز بودن فایل بنویس.
توضیح بده چرا ثبت پاکسازی باید بلافاصله پس از باز کردن فایل انجام شود.
مشخص کن اگر آمادهسازی بعد از باز شدن فایل با خطا مواجه شود، چه اتفاقی میافتد.
پست ۲۴۸ از دوره جامع Python 🐍
1👍1
🐍 آموزش جامع Python — پست ۲۴۹
تستکردن خروجی چاپشده با
گاهی تابعی مقدار مشخصی برنمیگرداند، بلکه نتیجه را با
در پایتون میتوانیم با
۱. تابعی برای نمایش اطلاعات
فرض کنید فایل
اجرای زیر:
این متن را چاپ میکند:
اما برای تست خودکار، بهتر است بهجای بررسی دستی ترمینال، خروجی را در تست دریافت کنیم.
۲. استفاده از
۳. بررسی بخشهای مهم کد
در طول تست،
تمام متن ذخیرهشده در جریان را بهصورت رشته برمیگرداند.
۴. چرا در انتهای خروجی
تابع
بنابراین:
معمولاً خروجی ثبتشده زیر را دارد:
اگر مقدار
در این حالت، خروجی دقیق برابر
۵. بررسی خروجی چندخطی
تابع زیر را در نظر بگیر:
تست آن میتواند به این شکل باشد:
توجه کنید که این متد تست باید داخل یک کلاس فرزند
⚠️ نکته مهم
جایگزینی
همچنین برای تست خروجی چاپشده، معمولاً بهتر است متن مورد انتظار را دقیق بررسی کنید؛ فاصلهها و خطهای جدید هم بخشی از خروجی هستند.
🎯 تمرین
تابعی به نام
سپس با
پست ۲۴۹ از دوره جامع Python 🐍
تستکردن خروجی چاپشده با
unittest.mock.patchگاهی تابعی مقدار مشخصی برنمیگرداند، بلکه نتیجه را با
print() نمایش میدهد. در چنین شرایطی، چگونه خروجی آن را آزمایش کنیم؟در پایتون میتوانیم با
unittest.mock.patch، جریان خروجی استاندارد یعنی sys.stdout را موقتاً جایگزین کنیم و متن چاپشده را بررسی کنیم.۱. تابعی برای نمایش اطلاعات
فرض کنید فایل
reports.py شامل این تابع است:def show_total(total):
print(f"Total: {total}")
اجرای زیر:
show_total(250)
این متن را چاپ میکند:
Total: 250
اما برای تست خودکار، بهتر است بهجای بررسی دستی ترمینال، خروجی را در تست دریافت کنیم.
۲. استفاده از
patch() و StringIOimport unittest
from unittest.mock import patch
from io import StringIO
from reports import show_total
class TestReports(unittest.TestCase):
@patch("sys.stdout", new_callable=StringIO)
def test_show_total(self, mock_stdout):
show_total(250)
self.assertEqual(
mock_stdout.getvalue(),
"Total: 250\n"
)
if __name__ == "__main__":
unittest.main()
۳. بررسی بخشهای مهم کد
StringIO چیست؟StringIO یک جریان متنی در حافظه است که میتوان روی آن نوشت و محتوایش را خواند؛ بدون اینکه فایل واقعی روی دیسک ایجاد شود.patch("sys.stdout", new_callable=StringIO) چه میکند؟در طول تست،
sys.stdout را با یک نمونه جدید از StringIO جایگزین میکند. بنابراین، فراخوانی print() متن را در این جریان مینویسد.getvalue() چه کاری انجام میدهد؟تمام متن ذخیرهشده در جریان را بهصورت رشته برمیگرداند.
۴. چرا در انتهای خروجی
\n داریم؟تابع
print() بهطور پیشفرض در پایان متن یک کاراکتر خط جدید اضافه میکند.بنابراین:
print("Python")معمولاً خروجی ثبتشده زیر را دارد:
"Python\n"
اگر مقدار
end را تغییر دهیم، نتیجه هم تغییر میکند:print("Python", end="!")در این حالت، خروجی دقیق برابر
"Python!" خواهد بود.۵. بررسی خروجی چندخطی
تابع زیر را در نظر بگیر:
def show_user(name, age):
print(f"Name: {name}")
print(f"Age: {age}")
تست آن میتواند به این شکل باشد:
@patch("sys.stdout", new_callable=StringIO)
def test_show_user(self, mock_stdout):
show_user("Sara", 25)
self.assertEqual(
mock_stdout.getvalue(),
"Name: Sara\nAge: 25\n"
)توجه کنید که این متد تست باید داخل یک کلاس فرزند
unittest.TestCase قرار بگیرد.⚠️ نکته مهم
جایگزینی
sys.stdout بر خروجیهایی اثر میگذارد که از همان جریان استفاده میکنند. اگر کد مستقیماً از جریان دیگری، مانند sys.stderr، استفاده کند، باید آن جریان را جداگانه بررسی کنید.همچنین برای تست خروجی چاپشده، معمولاً بهتر است متن مورد انتظار را دقیق بررسی کنید؛ فاصلهها و خطهای جدید هم بخشی از خروجی هستند.
🎯 تمرین
تابعی به نام
display_product(name, price) بنویس که این دو خط را چاپ کند:Product: Keyboard
Price: 1200
سپس با
patch() و StringIO تستی بنویس که خروجی کامل را بررسی کند.پست ۲۴۹ از دوره جامع Python 🐍
1👍1
🐍 آموزش جامع Python — پست ۲۵۰
تستکردن فایلها و پوشهها با
در پروژههای واقعی، توابع زیادی با فایلها و پوشهها کار میکنند؛ مثلاً خواندن تنظیمات، ذخیره گزارش یا پردازش فایلهای ورودی.
اما اگر تستها مستقیماً فایلهای واقعی پروژه را تغییر دهند، ممکن است اطلاعات مهمی از بین برود یا نتیجه تستها به وضعیت سیستم وابسته شود.
ماژول استاندارد
۱. ایجاد فایل موقت با
خروجی:
در این مثال:
با پایان بلوک
رفتار دقیق حذف فایل موقت به نوع تابع و سیستمعامل وابسته است.
۲. تست یک تابع که از مسیر فایل استفاده میکند
فرض کنید تابع زیر در فایل
میخواهیم بررسی کنیم که تابع محتوای فایل را درست میخواند؛ بدون اینکه به یک فایل تنظیمات واقعی وابسته باشیم.
نوشتن تست
🔍 چرا این روش مناسب است؟
۱.
۲. فایل
۳. تابع واقعی
۴. با پایان بلوک
در این تست از
۳. تفاوت ابزارهای مهم
ابزار
کاربرد
ایجاد فایل موقت برای کار مستقیم با محتوای آن
ایجاد فایل موقت با نام و مسیر قابلدسترسی
ایجاد پوشه موقت برای نگهداری یک یا چند فایل
نگهداری داده در حافظه تا رسیدن به آستانه مشخص و سپس انتقال به فایل موقت
نکته: اگر تابع مورد آزمایش به مسیر واقعی فایل نیاز دارد،
۴. یک اشتباه رایج
اگر تابع شما مسیر نسبی میگیرد، نباید فرض کنید فایل همیشه نسبت به پوشه پروژه پیدا میشود. مسیر نسبی معمولاً نسبت به پوشه کاری فعلی فرایند تفسیر میشود.
استفاده از مسیر صریح ساختهشده با
🎯 تمرین
تابعی به نام
سپس با
پست ۲۵۰ از دوره جامع Python 🐍
تستکردن فایلها و پوشهها با
tempfileدر پروژههای واقعی، توابع زیادی با فایلها و پوشهها کار میکنند؛ مثلاً خواندن تنظیمات، ذخیره گزارش یا پردازش فایلهای ورودی.
اما اگر تستها مستقیماً فایلهای واقعی پروژه را تغییر دهند، ممکن است اطلاعات مهمی از بین برود یا نتیجه تستها به وضعیت سیستم وابسته شود.
ماژول استاندارد
tempfile امکان ایجاد فایلها و پوشههای موقت را فراهم میکند تا تستها محیط مستقلتری داشته باشند.۱. ایجاد فایل موقت با
TemporaryFileimport 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 — پست ۲۵۱
تستکردن کدهای ناهمگام با
در پایتون، توابعی که با
کتابخانه استاندارد
🔹 ۱. یک تابع ناهمگام بسازیم
فراخوانی این تابع یک شیء coroutine ایجاد میکند. برای دریافت نتیجه، باید آن را اجرا و منتظر تکمیلش بمانیم.
🔹 ۲. نوشتن تست ناهمگام
🔍 نکات مهم
متد تست ناهمگام باید با
درون متد تست، برای انتظار نتیجه تابع ناهمگام از
در این مثال،
🔹 ۳. بررسی خطا در تابع ناهمگام
میتوانیم خطاهای مورد انتظار را نیز آزمایش کنیم:
تست:
این متد تست باید داخل کلاس
نکته: اگر از
🎯 تمرین
تابع ناهمگام زیر را پیادهسازی و آزمایش کن:
این تابع باید مربع عدد را برگرداند. سپس دو تست بنویس:
۱. بررسی کن که
۲. اگر تصمیم گرفتی ورودی منفی مجاز نباشد، برای آن
اجرای تستها:
پست ۲۵۱ از دوره جامع 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 — پست ۲۵۲
شبیهسازی توابع ناهمگام با
در پست قبل یاد گرفتیم توابع
ابزار
🔹 ۱. چرا به
فرض کنید تابعی برای دریافت اطلاعات کاربر از یک API داریم. هنگام تست نمیخواهیم درخواست واقعی اینترنتی ارسال کنیم؛ فقط میخواهیم رفتار تابع خودمان را بررسی کنیم.
فایل
این تابع به یک متد ناهمگام به نام
🔹 ۲. نوشتن تست با
🔍 توضیح کد
در این تست، هیچ درخواست واقعی به API ارسال نمیشود.
🔸 تفاوت
متد
چه چیزی را بررسی میکند؟
تابع یک بار با آرگومانهای مشخص فراخوانی شده است
فراخوانی ناهمگام یک بار با آرگومانهای مشخص مورد
برای بررسی رفتار ناهمگام، معمولاً
⚠️ نکته مهم
در کد بالا از
🎯 تمرین
تابعی به نام
سپس با
۱. نتیجه ساختگی
۲. نتیجه تابع را بررسی کند.
۳. تأیید کند
پست ۲۵۲ از دوره جامع 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() وابسته است.🔹 ۲. نوشتن تست با
AsyncMockimport 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 — پست ۲۵۳
تستکردن فراخوانیها با
در تستنویسی، گاهی فقط دانستن نتیجه یک تابع کافی نیست؛ باید بررسی کنیم تابعهای وابسته با چه آرگومانهایی و چند بار فراخوانی شدهاند.
در
🔹 ۱. بررسی آخرین فراخوانی با
خروجی:
ویژگی
🔹 ۲. بررسی تمام فراخوانیها با
خروجی:
این ویژگی تمام فراخوانیها را به ترتیب وقوع نگه میدارد.
🔹 ۳. بررسی آرگومانها
هر فراخوانی ثبتشده شامل دو بخش است:
مثال:
خروجی:
وجود ویرگول در
🔹 ۴. استفاده در تست واقعی
فرض کنیم تابع زیر را داریم:
تست آن:
در این تست، علاوه بر تعداد فراخوانیها، ترتیب و آرگومانهای هر فراخوانی نیز بررسی میشود.
⚠️ نکته مهم
اگر فقط میخواهید تعداد فراخوانیها را بررسی کنید، از
همچنین
🎯 تمرین
تابعی به نام
سپس با
۱. تعداد فراخوانیها برابر تعداد اعضای فهرست باشد.
۲. آرگومانهای هر فراخوانی درست باشند.
۳. ترتیب فراخوانیها مطابق ترتیب فهرست ورودی باشد.
پست ۲۵۳ از دوره جامع Python 🐍
تستکردن فراخوانیها با
call_args و call_args_listدر تستنویسی، گاهی فقط دانستن نتیجه یک تابع کافی نیست؛ باید بررسی کنیم تابعهای وابسته با چه آرگومانهایی و چند بار فراخوانی شدهاند.
در
unittest.mock ابزارهایی مانند call_args و call_args_list این امکان را فراهم میکنند.🔹 ۱. بررسی آخرین فراخوانی با
call_argsfrom 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_listfrom 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 نباید جای تست رفتار واقعی سیستم را بگیرد؛ Mock برای بررسی تعامل با وابستگیها مناسب است.
🎯 تمرین
تابعی به نام
۱.
۲.
سپس با
پست ۲۵۴ از دوره جامع 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_callsfrom 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 باعث میشود تستها دقیقتر باشند و رفتار واقعی برنامه را بهتر شبیهسازی کنند.
سه ابزار مهم در
🔹 ۱. کلاس
خروجی:
با
نکته: ویژگیهای ساختگی مانند
🔹 ۲. کلاس
برای مثال، میتوانیم رفتار
خروجی:
این قابلیت برای تست کدهایی مفید است که با عملیات ویژه پایتون کار میکنند.
البته نتیجههای بالا ساختگیاند؛ این کد یک فهرست واقعی با سه عضو ایجاد نمیکند.
🔹 ۳. کلاس
گاهی میخواهیم یک شیء ساختگی داشته باشیم که قابل فراخوانی نباشد؛ یعنی نتوان آن را مانند تابع صدا زد.
خروجی:
اما این دستور خطا ایجاد میکند:
زیرا
🔍 مقایسه سریع
ویژگی
ساخت ویژگیهای ساختگی
بله
بله
بله
پشتیبانی آماده از بسیاری از متدهای ویژه
محدودتر
بله
محدودتر
قابل فراخوانی مانند تابع
بله
بله
خیر
کاربرد اصلی
شبیهسازی وابستگیها
شبیهسازی اشیای دارای رفتارهای ویژه
شبیهسازی اشیای غیرقابلفراخوانی
⚠️ نکته مهم: محدودکردن رفتار ساختگی
اگر بخواهیم Mock فقط ویژگیهای موجود در یک شیء واقعی را بپذیرد، میتوانیم از
اما دسترسی به ویژگی تعریفنشدهای مانند
🎯 تمرین
یک کلاس
۱. با
۲. با
۳. با
پست ۲۵۵ از دوره جامع Python 🐍
تفاوت
Mock، MagicMock و NonCallableMockدر تستنویسی پایتون، همه اشیای ساختگی رفتار یکسانی ندارند. انتخاب نوع مناسب Mock باعث میشود تستها دقیقتر باشند و رفتار واقعی برنامه را بهتر شبیهسازی کنند.
سه ابزار مهم در
unittest.mock عبارتاند از:MockMagicMockNonCallableMock🔹 ۱. کلاس
MockMock برای شبیهسازی اشیا و بررسی تعامل با آنها استفاده میشود.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 اعمال شده باشد.🔹 ۲. کلاس
MagicMockMagicMock مانند 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 برای فراخوانی مانند تابع طراحی نشده است.🔍 مقایسه سریع
ویژگی
MockMagicMockNonCallableMockساخت ویژگیهای ساختگی
بله
بله
بله
پشتیبانی آماده از بسیاری از متدهای ویژه
محدودتر
بله
محدودتر
قابل فراخوانی مانند تابع
بله
بله
خیر
کاربرد اصلی
شبیهسازی وابستگیها
شبیهسازی اشیای دارای رفتارهای ویژه
شبیهسازی اشیای غیرقابلفراخوانی
⚠️ نکته مهم: محدودکردن رفتار ساختگی
اگر بخواهیم 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 کتاب رو خریداری کنم و در اختیار شما قرار بدم تا همه بتونیم از این منبع آموزشی استفاده کنیم.
💻 یادگیری رو ادامه بدید؛ هر قدم کوچیک، شما رو به یک برنامهنویس بهتر تبدیل میکنه!
سلام به همه برنامهنویسها! 👋
یک کتاب آموزش پایتون پیدا کردم و گفتم اون رو با شما هم به اشتراک بگذارم تا اگر دوست داشتید، بتونید تهیهاش کنید و برای یادگیری بیشتر ازش استفاده کنید.
📖 نسخه الکترونیکی: 🔗 دریافت از طاقچه: https://taaghche.com/book/251603
📦 نسخه فیزیکی: میتونید کتاب رو بهصورت چاپی هم از دیجیکالا تهیه کنید.
💙 من هم سعی میکنم نسخه PDF کتاب رو خریداری کنم و در اختیار شما قرار بدم تا همه بتونیم از این منبع آموزشی استفاده کنیم.
💻 یادگیری رو ادامه بدید؛ هر قدم کوچیک، شما رو به یک برنامهنویس بهتر تبدیل میکنه!
👍1
🐍 راهنمای پرسش و رفع اشکال دوره Python
دوستان، اگر در طول دوره در هر بخش از آموزش، کدها، تمرینها یا اجرای برنامه به مشکلی برخوردید، میتوانید سؤال خودتان را مطرح کنید تا بررسی شود.
💬 چه زمانی پیام بدهیم؟
اگر در مواردی مثل اینها مشکل داشتید:
🔹 مفهوم یک مبحث را متوجه نشدید
🔹 کدی که نوشتهاید اجرا نمیشود
🔹 با یک Error / Exception مواجه شدید
🔹 خروجی برنامه با چیزی که انتظار داشتید متفاوت است
🔹 در انجام تمرینهای دوره مشکل دارید
🔹 نمیدانید یک بخش از کد چگونه کار میکند
🔹 در نصب Python یا اجرای برنامهها مشکلی دارید
🔹 درباره یک بخش از آموزش نیاز به توضیح بیشتری دارید
🔹 در کد خودتان با یک رفتار غیرمنتظره مواجه شدید
میتوانید مشکل را مطرح کنید.
📌 برای بررسی سریعتر مشکل
لطفاً هنگام ارسال سؤال، تا حد امکان این موارد را هم بفرستید:
1️⃣ کدی که نوشتهاید
2️⃣ خطایی که دریافت میکنید
اگر Error نمایش داده میشود، متن کامل آن را ارسال کنید.
3️⃣ توضیح کوتاه درباره مشکل
مثلاً:
این اطلاعات کمک میکند مشکل دقیقتر بررسی شود.
🕐 ساعت پاسخگویی
برای پاسخگویی و رفع اشکال، هر روز در بازه:
🌙 ساعت ۱۱ تا ۱۲ شب
در خدمت شما هستیم.
اگر خارج از این ساعت پیام ارسال کردید، ممکن است پاسخگویی با تأخیر انجام شود؛ اما پیام شما بررسی خواهد شد.
🆘 راه ارتباطی
اگر در کد، تمرین، آموزش یا اجرای برنامه به مشکل خوردید، از طریق آیدی زیر پیام دهید:
👉 @JarvisAI_77
لطفاً سؤال خودتان را تا حد امکان واضح و همراه با کد و متن خطا ارسال کنید تا بتوانیم دقیقتر راهنمایی کنیم.
📚 هدف این است که هیچ مشکلی در مسیر یادگیری Python باعث متوقف شدن ادامه مسیرتان نشود.
سؤال بپرسید، مشکل را پیدا کنید و با هم حلش کنیم. 🐍
دوستان، اگر در طول دوره در هر بخش از آموزش، کدها، تمرینها یا اجرای برنامه به مشکلی برخوردید، میتوانید سؤال خودتان را مطرح کنید تا بررسی شود.
💬 چه زمانی پیام بدهیم؟
اگر در مواردی مثل اینها مشکل داشتید:
🔹 مفهوم یک مبحث را متوجه نشدید
🔹 کدی که نوشتهاید اجرا نمیشود
🔹 با یک Error / Exception مواجه شدید
🔹 خروجی برنامه با چیزی که انتظار داشتید متفاوت است
🔹 در انجام تمرینهای دوره مشکل دارید
🔹 نمیدانید یک بخش از کد چگونه کار میکند
🔹 در نصب Python یا اجرای برنامهها مشکلی دارید
🔹 درباره یک بخش از آموزش نیاز به توضیح بیشتری دارید
🔹 در کد خودتان با یک رفتار غیرمنتظره مواجه شدید
میتوانید مشکل را مطرح کنید.
📌 برای بررسی سریعتر مشکل
لطفاً هنگام ارسال سؤال، تا حد امکان این موارد را هم بفرستید:
1️⃣ کدی که نوشتهاید
# کد خودتان را اینجا قرار دهید
2️⃣ خطایی که دریافت میکنید
اگر Error نمایش داده میشود، متن کامل آن را ارسال کنید.
3️⃣ توضیح کوتاه درباره مشکل
مثلاً:
انتظار داشتم برنامه عدد 20 را چاپ کند، اما 0 چاپ میشود.
این اطلاعات کمک میکند مشکل دقیقتر بررسی شود.
🕐 ساعت پاسخگویی
برای پاسخگویی و رفع اشکال، هر روز در بازه:
🌙 ساعت ۱۱ تا ۱۲ شب
در خدمت شما هستیم.
اگر خارج از این ساعت پیام ارسال کردید، ممکن است پاسخگویی با تأخیر انجام شود؛ اما پیام شما بررسی خواهد شد.
🆘 راه ارتباطی
اگر در کد، تمرین، آموزش یا اجرای برنامه به مشکل خوردید، از طریق آیدی زیر پیام دهید:
👉 @JarvisAI_77
لطفاً سؤال خودتان را تا حد امکان واضح و همراه با کد و متن خطا ارسال کنید تا بتوانیم دقیقتر راهنمایی کنیم.
📚 هدف این است که هیچ مشکلی در مسیر یادگیری Python باعث متوقف شدن ادامه مسیرتان نشود.
سؤال بپرسید، مشکل را پیدا کنید و با هم حلش کنیم. 🐍
👍2
🐍 آموزش جامع Python — پست ۲۵۶
محدودکردن Mock با
در پست قبل دیدیم که
برای مثال، ممکن است نام یک متد را اشتباه بنویسیم و Mock بدون هشدار آن را ایجاد کند. در نتیجه، تست با وجود اشتباه در کد همچنان موفق شود!
برای جلوگیری از این مشکل، سه قابلیت مهم داریم.
🔹 ۱. استفاده از
با
چون
🔹 ۲. استفاده از
در این حالت، علاوه بر جلوگیری از دسترسی به ویژگیهای تعریفنشده، نمیتوان ویژگی جدیدی به Mock اضافه کرد.
🔹 ۳. استفاده از
قابلیت
در این مثال،
🔍 مقایسه سه قابلیت
⚠️ نکات مهم
هیچکدام جایگزین تست رفتار واقعی برنامه نیستند.
🎯 تمرین
کلاس زیر را در نظر بگیر:
با
۱. فراخوانی
۲. فراخوانی
۳. فراخوانی متد تعریفنشده
هدف این تمرین، ساخت Mockهایی است که به ساختار واقعی برنامه نزدیکتر باشند.
پست ۲۵۶ از دوره جامع 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_setspec_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 با
در پروژههای بزرگ ممکن است یک ماژول چندین وابستگی خارجی داشته باشد. اگر بخواهیم همه آنها را هنگام تست جایگزین کنیم، استفاده از چندین
در چنین شرایطی میتوانیم از
🔹 ۱. آشنایی با
این ابزار امکان جایگزینی چند ویژگی از یک ماژول یا شیء را بهصورت همزمان فراهم میکند.
فرض کنید فایل
میخواهیم هنگام تست، هر دو سرویس ارسال را شبیهسازی کنیم.
🔹 ۲. نوشتن تست
🔍 بررسی کد
چند ویژگی از ماژول
به
در این مثال، Mockهای ایجادشده بهعنوان آرگومان به متد تست تحویل داده میشوند.
بررسی میکند هر سرویس دقیقاً یک بار با آرگومان مورد انتظار فراخوانی شده باشد.
🔹 ۳. استفاده با
میتوانیم بهجای Decorator از ساختار
در این روش، دیکشنری
⚠️ نکته مهم
جایگزینیها فقط در محدوده اجرای Patch فعال هستند.
پس از خروج از بلوک
اگر فقط یک وابستگی دارید، معمولاً
اگر نام ویژگیها از قبل مشخص باشد،
🎯 تمرین
ماژولی با سه تابع زیر در نظر بگیر:
تابعی به نام
سپس با
پست ۲۵۷ از دوره جامع 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 — پست ۲۵۸
مدیریت موقت تغییرات با
در تستنویسی گاهی لازم است مقادیر یک دیکشنری را موقتاً تغییر دهیم؛ مثلاً متغیرهای محیطی (Environment Variables)، تنظیمات برنامه یا دادههای پیکربندی.
برای این کار میتوانیم از
🔹 ۱. آشنایی با
فرض کنید برنامه ما تنظیمات را از یک دیکشنری دریافت میکند:
میخواهیم هنگام اجرای تست، مقدار
خروجی:
پس از پایان بلوک
🔹 ۲. استفاده برای تست متغیرهای محیطی
یکی از کاربردهای مهم این ابزار، مدیریت متغیرهای محیطی با
فرض کنید تابع زیر را داریم:
حالا میتوانیم بدون تغییر دائمی تنظیمات سیستم، آن را آزمایش کنیم:
🔍 بررسی پارامترها
آرگومان اول: دیکشنری موردنظر برای تغییر.
آرگومان دوم: مقادیری که باید موقتاً اضافه یا جایگزین شوند.
در تست دوم، از
⚠️ نکات مهم
۱. تغییرات
۲. این ابزار برای دیکشنریهای معمولی و نگاشتهایی مانند
۳. اگر چند تست همزمان به یک متغیر محیطی دسترسی داشته باشند، تغییرات موقت ممکن است باعث تداخل شوند؛ بنابراین مدیریت تستهای موازی اهمیت دارد.
۴.
🎯 تمرین
تابعی به نام
اگر این متغیر وجود نداشت، مقدار زیر را برگرداند:
سپس با
وقتی
وقتی این متغیر وجود ندارد، مقدار پیشفرض برگردد.
پست ۲۵۸ از دوره جامع 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 با
در تستنویسی، گاهی لازم است یک تابع ساختگی (
این ویژگی سه کاربرد مهم دارد:
۱. شبیهسازی وقوع خطا
میتوانیم مشخص کنیم که فراخوانی Mock یک استثنا ایجاد کند:
خروجی:
۲. برگرداندن پاسخهای متوالی
میتوانیم یک لیست به
خروجی:
هر بار که Mock فراخوانی میشود، یک مقدار از لیست مصرف میشود. اگر مقادیر تمام شوند،
۳. اجرای تابع سفارشی
میتوانیم یک تابع واقعی را بهعنوان
خروجی:
در این حالت، آرگومانهای ارسالشده به Mock به تابع
تفاوت
نکته: اگر
🎯 تمرین
یک Mock برای شبیهسازی درخواست به سرور بسازید که:
در فراخوانی اول خطای
در فراخوانی دوم مقدار
با استفاده از
پست ۲۵۹ از دوره جامع 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_effectreturn_value: یک مقدار ثابت برای فراخوانیهای Mock تعیین میکند.side_effect: امکان ایجاد خطا، برگرداندن مقادیر متوالی یا اجرای منطق سفارشی را فراهم میکند.نکته: اگر
side_effect یک تابع باشد، میتواند مقدار ویژه DEFAULT را برگرداند تا Mock از return_value استفاده کند.🎯 تمرین
یک Mock برای شبیهسازی درخواست به سرور بسازید که:
در فراخوانی اول خطای
ConnectionError ایجاد کند.در فراخوانی دوم مقدار
"Success" را برگرداند.با استفاده از
try/except خطای اول را مدیریت کنید.پست ۲۵۹ از دوره جامع Python 🐍
👍1
🐍 آموزش Python | پست ۲۶۰
استفاده از
در پستهای قبلی با
۱. جایگزینی موقت یک متد
فرض کنید کلاسی داریم که اطلاعات کاربر را از یک سرویس دریافت میکند:
میتوانیم در تست، متد
در این مثال:
داخل بلوک
پس از پایان بلوک، رفتار اصلی متد بازگردانده میشود.
۲. جایگزینی ویژگی یک کلاس
خروجی:
۳. تفاوت
ابزار
کاربرد
جایگزینی یک شیء با استفاده از مسیر ماژولی
جایگزینی ویژگی یا متد یک شیء یا کلاس مشخص
نکته مهم: هر دو ابزار پس از پایان محدوده اجرا، مقدار یا رفتار اولیه را بازمیگردانند؛ بنابراین برای تغییرات موقت در تستها بسیار کاربردی هستند.
🎯 تمرین
کلاسی به نام
سپس با استفاده از
بررسی کنید که پس از پایان
پست ۲۶۰ از دوره جامع 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 با
تا اینجا از
برای این کار از متدهای
۱. استفاده از
با فراخوانی
خروجی:
۲. استفاده از
برای بازگرداندن رفتار اصلی، باید
خروجی:
با اجرای
۳. مدیریت Patch در تستهای unittest
اگر در چند تست به یک Patch نیاز داشته باشیم، میتوانیم آن را در
چرا
چون اگر در طول تست خطایی رخ دهد، پاکسازی ثبتشده همچنان اجرا میشود و Patch در محیط تست باقی نمیماند.
نکات مهم
هر
فراموشکردن
برای مدیریت خودکار محدوده Patch، استفاده از
در تستهای
🎯 تمرین
یک کلاس به نام
سپس با
پست ۲۶۱ از دوره جامع 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