unittest.mock کتابخانهای برای آزمون در پایتون است. این کتابخانه به شما امکان میدهد بخشهایی از سیستم تحت آزمون خود را با اشیای ماک جایگزین کنید و ادعاهایی درباره چگونگی استفاده از آنها مطرح کنید.
unittest.mock یک کلاس Mock اصلی ارائه میدهد که نیاز به ایجاد قطعههای جایگزین (stubs) زیادی در سراسر بدنه آزمون شما را برطرف میکند. پس از انجام یک عمل، میتوانید ادعاهایی دربارهی اینکه کدام متدها / ویژگیها استفادهشدهاند و با چه آرگومانهایی فراخوانیشدهاند، مطرح کنید. همچنین میتوانید مقادیر بازگشتی را مشخص کنید و ویژگیهای مورد نیاز را به روش عادی تنظیم کنید.
علاوه بر این، ماک یک دکوراتور patch() ارائه میدهد که وصله کردن ویژگیهای سطح ماژول و کلاس را در محدوده یک آزمون مدیریت میکند، به همراه sentinel برای ایجاد اشیای یکتا. برای چند مثال از نحوه استفاده از Mock، MagicMock و patch()، به quick guide مراجعه کنید.
ماک برای استفاده با unittest طراحی شده است و بهجای الگوی «ضبط -> بازپخش» که در بسیاری از چارچوبهای ماک استفاده میشود، بر الگوی «عمل -> ادعا» مبتنی است.
یک بکپورت از unittest.mock برای نسخههای پیشین پایتون وجود دارد که بهعنوان mock در PyPI در دسترس است.
اشیای Mock و MagicMock همه ویژگیها و متدها را هنگامی که به آنها دسترسی پیدا میکنید ایجاد میکنند و جزئیات نحوه استفاده از خود را ذخیره میکنند. شما میتوانید آنها را پیکربندی کنید تا مقادیر بازگشتی را مشخص کنید یا ویژگیهای در دسترس را محدود کنید، و سپس ادعاهایی درباره نحوه استفاده از آنها مطرح کنید:
ماک راههای دیگر بسیاری دارد که میتوانید آن را پیکربندی کنید و رفتار آن را کنترل نمایید. برای مثال، آرگومان spec ماک را پیکربندی میکند تا مشخصات خود را از شیء دیگری بگیرد. تلاش برای دسترسی به ویژگیها یا متدهایی در ماک که در spec وجود ندارند، با AttributeError شکست خواهد خورد.
دکوراتور patch() / مدیر زمینه، ماک کردن کلاسها یا اشیاء در یک ماژول تحت آزمون را آسان میسازد. شیءای که شما مشخص میکنید، در طول آزمون با یک ماک (یا شیء دیگر) جایگزین میشود و هنگامی که آزمون پایان مییابد بازگردانده میشود:
هنگامی که دکوراتورهای patch را تودرتو میکنید، ماکها به همان ترتیبی که اعمال میشوند، به تابع دکوراتشده داده میشوند (ترتیب عادی Python برای اعمال دکوراتورها). این یعنی از پایین به بالا، بنابراین در مثال بالا، ماک برای module.ClassName1 ابتدا داده میشود.
با patch()، مهم است که اشیاء را در فضای نامی که در آن جستجو میشوند وصله (patch) کنید. این کار معمولاً ساده است، اما برای یک راهنمای سریع محل وصلهکردن را بخوانید.
علاوه بر استفاده بهعنوان دکوراتور، patch() میتواند بهعنوان مدیر زمینه در یک دستور with استفاده شود:
Mock از ماک کردن متدهای جادویی پایتون پشتیبانی میکند. سادهترین راه برای استفاده از متدهای ویژه، استفاده از کلاس MagicMock است. این کلاس به شما امکان میدهد کارهایی مانند موارد زیر را انجام دهید:
ماک به شما اجازه میدهد توابع (یا دیگر نمونههای ماک) را به متدهای جادویی اختصاص دهید و آنها بهدرستی فراخوانی میشوند. کلاس MagicMock صرفاً گونهای از ماک است که تمام متدهای ویژه آن از پیش برای شما ایجاد شدهاند (البته بههرحال همهی متدهای مفید).
در زیر مثالی از استفاده از متدهای جادویی با کلاس معمولی Mock آمده است:
برای اطمینان از اینکه اشیای ماک در آزمونهای شما همان API اشیایی را دارند که جایگزین آنها میشوند، میتوانید از auto-speccing استفاده کنید. Auto-speccing را میتوان از طریق آرگومان autospec در patch، یا تابع create_autospec() انجام داد. Auto-speccing اشیای ماکی ایجاد میکند که همان ویژگیها و متدهای اشیایی را دارند که جایگزین آنها میشوند، و همه توابع و متدها (از جمله سازندهها) همان امضای فراخوانی شیء واقعی را دارند.
این موضوع تضمین میکند که ماکهای شما در صورت استفادهی نادرست، به همان شیوهای که آپکد شما شکست میخورد، شکست خواهند خورد:
همچنین میتوان از create_autospec() برای کلاسها نیز استفاده کرد، که در این حالت امضای متد __init__ را کپی میکند، و برای اشیاء قابل فراخوانی، که در این حالت امضای متد __call__ را کپی میکند.
Mock یک شیء ماک انعطافپذیر است که برای جایگزینی استفاده از جایگزینهای موقت (stubs) و جایگزینهای آزمون (test doubles) در سراسر کد شما در نظر گرفته شده است. ماکها فراخوانیپذیر هستند و هنگامی که به ویژگیهای آنها دسترسی پیدا میکنید، آن ویژگیها را بهعنوان ماکهای جدید ایجاد میکنند [1]. دسترسی به همان ویژگی همیشه همان ماک را برمیگرداند. ماکها نحوه استفاده شما از آنها را ثبت میکنند و به شما امکان میدهند درباره آنچه کد شما با آنها انجام داده است، ادعاهایی مطرح کنید.
MagicMock یک زیرکلاس از Mock است که تمام متدهای جادویی آن از پیش ساختهشدهاند و آماده استفاده هستند. همچنین گونههای غیرفراخوانیپذیر نیز وجود دارند که هنگام ماککردن اشیایی که فراخوانیپذیر نیستند، مفید هستند: NonCallableMock و NonCallableMagicMock
دکوراتورهای patch() جایگزینی موقت کلاسها در یک ماژول خاص با یک شیء Mock را آسان میکنند. بهطور پیشفرض، patch() یک MagicMock برای شما ایجاد میکند. شما میتوانید با استفاده از آرگومان new_callable در patch()، یک کلاس جایگزین برای Mock را مشخص کنید.
یک شیء Mock جدید ایجاد کنید. Mock چند آرگومان اختیاری میگیرد که رفتار شیء Mock را مشخص میکنند:
spec: این میتواند یک فهرست از رشتهها یا یک شیء موجود (یک کلاس یا نمونه) باشد که بهعنوان مشخصات برای شیء ماک عمل میکند. اگر یک شیء را بهعنوان ورودی بدهید، یک فهرست از رشتهها با فراخوانی dir روی آن شیء تشکیل میشود (بهجز ویژگیها و متدهای ویژه پشتیبانینشده). دسترسی به هر ویژگی که در این فهرست نباشد، باعث پرتاب AttributeError میشود.
اگر spec یک شیء باشد (نه فهرستی از رشتهها)، آنگاه __class__ کلاس شیء spec را برمیگرداند. این به ماکها اجازه میدهد آزمونهای isinstance() را بگذرانند.
spec_set: گونهای سختگیرانهتر از spec. در صورت استفاده، تلاش برای set کردن یا دریافت یک ویژگی روی ماک که آن ویژگی روی شیء ارسالشده بهعنوان spec_set وجود ندارد، باعث پرتاب AttributeError میشود.
side_effect: تابعی که هرگاه ماک فراخوانی شود، فراخوانی میشود. ویژگی side_effect را ببینید. برای پرتاب استثناها یا تغییر مقادیر بازگشتی بهصورت پویا مفید است. این تابع با همان آرگومانهای ماک فراخوانی میشود، و مگر اینکه DEFAULT را برگرداند، مقدار بازگشتی این تابع بهعنوان مقدار بازگشتی استفاده میشود.
بهعنوان جایگزین، side_effect میتواند یک کلاس یا نمونه استثنا باشد. در این صورت، هنگام فراخوانی ماک، استثنا پرتاب میشود.
اگر side_effect یک پیمایشپذیر باشد، هر فراخوانی ماک مقدار بعدی را از آن پیمایشپذیر برمیگرداند.
میتوان یک side_effect را با تنظیم آن به None پاک کرد.
return_value: مقداری که هنگام فراخوانی ماک برگردانده میشود. بهطور پیشفرض، این یک Mock جدید است (که در اولین دسترسی ایجاد میشود). ویژگی return_value را ببینید.
unsafe: بهطور پیشفرض، دسترسی به هر ویژگی که نام آن با assert، assret، asert، aseert یا assrt آغاز میشود، باعث پرتاب استثنای AttributeError میشود. ارسال unsafe=True دسترسی به این ویژگیها را مجاز میکند.
اضافه شده در نسخهی 3.5.
wraps: آیتمی که شیء ماک باید آن را پوشش دهد. اگر wraps برابر None نباشد، فراخوانی Mock فراخوانی را به شیء پوششدادهشده منتقل میکند (نتیجه واقعی را برمیگرداند). دسترسی به ویژگی روی ماک، یک شیء Mock برمیگرداند که ویژگی متناظر شیء پوششدادهشده را پوشش میدهد (بنابراین تلاش برای دسترسی به ویژگیای که وجود ندارد، باعث پرتاب AttributeError میشود).
اگر برای ماک یک return_value صریح تنظیم شده باشد، فراخوانیها به شیء پوشیدهشده منتقل نمیشوند و در عوض return_value برگردانده میشود.
name: اگر ماک دارای نام باشد، از آن نام در repr ماک استفاده میشود. این موضوع میتواند برای اشکالزدایی مفید باشد. نام به ماکهای فرزند منتقل میشود.
همچنین میتوان ماکها را با آرگومانهای کلیدواژهای دلخواه فراخوانی کرد. این آرگومانها برای تنظیم ویژگیهای ماک پس از ایجاد آن استفاده میشوند. برای جزئیات، متد configure_mock() را ببینید.
بررسی کنید که ماک با آرگومانهای مشخصشده فراخوانی شده است.
این ادعا (assert) در صورتی موفق میشود که ماک تاکنون فراخوانی شده باشد، برخلاف assert_called_with() و assert_called_once_with() که فقط در صورتی موفق میشوند که فراخوانی، آخرین فراخوانی باشد، و در مورد assert_called_once_with() باید تنها فراخوانی نیز باشد.
>>> m=Mock()>>> m.hello.assert_not_called()>>> obj=m.hello()>>> m.hello.assert_not_called()Traceback (most recent call last):...AssertionError: Expected 'hello' to not have been called. Called 1 times.Calls: [call()].
توجه داشته باشید که reset_mock() بهطور پیشفرض return_value، side_effect یا هیچکدام از ویژگیهای فرزند را که با انتساب معمولی تنظیم کردهاید، پاک نمیکند.
ماکهای فرزند نیز بازنشانی میشوند.
تغییر یافته در نسخهی 3.6: دو آرگومان فقطکلیدواژهای به تابع reset_mock افزوده شد.
مشخصات را به یک ماک اضافه کنید. spec میتواند یک شیء یا فهرستی از رشتهها باشد. فقط ویژگیهای موجود در spec را میتوان بهعنوان ویژگی از ماک واکشی کرد.
اگر spec_set درست باشد، تنها ویژگیهای موجود در spec قابل تنظیم هستند.
یک ماک را بهعنوان ویژگی این ماک متصل کنید و نام و والد آن را جایگزین نمایید. فراخوانیهای ماک متصلشده در ویژگیهای method_calls و mock_calls این ماک ثبت خواهند شد.
ویژگیهای ماک را از طریق آرگومانهای کلیدواژهای تنظیم کنید.
میتوان ویژگیها بههمراه مقادیر بازگشتی و عوارض جانبی را روی ماکهای فرزند با استفاده از نماد نقطهای استاندارد و واگشایی یک دیکشنری در فراخوانی متد تنظیم کرد:
ماکهای فرزند را برای ویژگیها و مقدار بازگشتی ایجاد میکند. بهطور پیشفرض، ماکهای فرزند از همان نوع والد خواهند بود. زیرکلاسهای Mock ممکن است بخواهند این را بازنویسی کنند تا نحوهی ساخت ماکهای فرزند را سفارشیسازی کنند.
برای ماکهای غیرفراخوانیپذیر، از نوع فراخوانیپذیر استفاده خواهد شد (بهجای هر زیرکلاس سفارشی).
این میتواند یک تابع برای فراخوانی هنگام فراخوانی ماک، یک پیمایشپذیر، یا یک استثنا (کلاس یا نمونه) برای پرتاب باشد.
اگر تابعی را بهعنوان ورودی بدهید، آن تابع با همان آرگومانهایی که ماک دریافت میکند فراخوانی میشود و مگر آنکه تابع شیء یکتای DEFAULT را برگرداند، فراخوانی ماک هر مقداری را که تابع برگرداند برمیگرداند. اگر تابع DEFAULT را برگرداند، آنگاه ماک مقدار معمول خود را (از return_value) برمیگرداند.
اگر یک پیمایشپذیر به آن بدهید، از آن برای گرفتن یک پیمایشگر استفاده میشود که باید در هر فراخوانی یک مقدار تولید کند. این مقدار میتواند یک نمونه استثنا باشد که پرتاب میشود، یا مقداری که از فراخوانی ماک بازگردانده میشود (مدیریت DEFAULT مشابه حالت تابع است).
مثالی از یک ماک که استثنایی را پرتاب میکند (برای آزمون مدیریت استثنا در یک API):
این مقدار یا None است (اگر ماک فراخوانینشده باشد)، یا آرگومانهایی که ماک آخرین بار با آنها فراخوانی شده است. این مقدار در قالب یک تاپل خواهد بود: عضو اول، که میتوان از طریق ویژگی args نیز به آن دسترسی داشت، آرگومانهای جایگاهی است که ماک با آنها فراخوانی شده است (یا یک تاپل خالی) و عضو دوم، که میتوان از طریق ویژگی kwargs نیز به آن دسترسی داشت، آرگومانهای کلیدواژهای است (یا یک دیکشنری خالی).
این فهرستی از تمام فراخوانیهای انجامشده به شیء ماک به ترتیب است (بنابراین طول فهرست برابر با تعداد دفعاتی است که شیء ماک فراخوانی شده است). پیش از انجام هرگونه فراخوانی، این فهرست خالی است. میتوان از شیء call برای ساختن آسان فهرستهایی از فراخوانیها جهت مقایسه با call_args_list استفاده کرد.
عناصر mock_calls اشیای call هستند. میتوانید اینها را بهصورت تاپل واگشایی کنید تا به تکتک آرگومانها دسترسی پیدا کنید. فراخوانیها بهصورت تاپل را ببینید.
نکته
نحوهی ثبت mock_calls به این معناست که هرگاه فراخوانیهای تودرتو انجام شوند، پارامترهای فراخوانیهای بالادستی ثبت نمیشوند و بنابراین همیشه در مقایسه برابر خواهند بود:
معمولاً ویژگی __class__ یک شیء، نوع آن را برمیگرداند. برای یک شیء ماک دارای spec، ویژگی __class__ در عوض کلاس مشخصشده در spec را برمیگرداند. این امکان را به اشیاء ماک میدهد تا آزمونهای isinstance() را برای شیءای که جایگزین آن هستند / خود را بهجای آن جا میزنند، با موفقیت بگذرانند:
>>> mock=Mock(spec=3)>>> isinstance(mock,int)True
__class__ قابل انتساب است؛ این به یک ماک اجازه میدهد بدون اینکه شما را مجبور به استفاده از spec کند، بررسی isinstance() را بگذراند:
نسخهای غیرفراخوانیپذیر از Mock. پارامترهای سازنده همان معنای پارامترهای Mock را دارند، بهجز return_value و side_effect که در یک ماک غیرفراخوانیپذیر معنایی ندارند.
اشیای ماک که از یک کلاس یا نمونه بهعنوان spec یا spec_set استفاده میکنند، میتوانند آزمونهای isinstance() را بگذرانند:
کلاسهای Mock از ماک کردن متدهای جادویی پشتیبانی میکنند. برای جزئیات کامل، متدهای جادویی را ببینید.
کلاسهای ماک و دکوراتورهای patch() همگی آرگومانهای کلیدواژهای دلخواه را برای پیکربندی میپذیرند. در دکوراتورهای patch()، کلیدواژهها به سازندهی ماکِ در حال ایجاد ارسال میشوند. آرگومانهای کلیدواژهای برای پیکربندی ویژگیهای ماک هستند:
مقدار بازگشتی و اثر جانبی ماکهای فرزند را میتوان به همین روش، با استفاده از نمادگذاری نقطهای تنظیم کرد. از آنجا که نمیتوانید از نامهای نقطهای مستقیماً در یک فراخوانی استفاده کنید، باید یک دیکشنری ایجاد کنید و آن را با استفاده از ** واگشایی کنید:
یک ماک فراخوانیپذیر که با یک spec (یا یک spec_set) ایجاد شده است، هنگام تطبیق فراخوانیها با ماک، امضای شیء مشخصات را دروننگری میکند. بنابراین، میتواند آرگومانهای فراخوانی واقعی را صرفنظر از اینکه بهصورت جایگاهی یا با نام ارسال شده باشند، تطبیق دهد:
ماکی که برای استفاده بهعنوان property، یا descriptor دیگری، روی یک کلاس در نظر گرفته شده است. PropertyMock متدهای __get__() و __set__() را فراهم میکند تا بتوانید یک مقدار بازگشتی را هنگام واکشی آن مشخص کنید.
بازیابی یک نمونه PropertyMock از یک شیء، ماک را بدون آرگومان فراخوانی میکند. تنظیم آن، ماک را با مقداری که تنظیم میشود فراخوانی میکند.
به دلیل نحوهی ذخیرهسازی ویژگیهای ماک، نمیتوانید یک PropertyMock را بهطور مستقیم به یک شیء ماک متصل کنید. در عوض میتوانید آن را به شیء نوع ماک متصل کنید:
نسخهای ناهمگام از MagicMock. شیء AsyncMock بهگونهای رفتار میکند که بهعنوان یک تابع ناهمگام شناخته میشود، و نتیجهی یک فراخوانی قابل await (awaitable) است.
نتیجهی mock() یک تابع ناهمگام است که پس از اینکه await شد، نتیجهی side_effect یا return_value را خواهد داشت:
اگر side_effect یک تابع باشد، تابع ناهمگام نتیجهی آن تابع را برمیگرداند،
اگر side_effect یک استثنا باشد، تابع ناهمگام آن استثنا را پرتاب خواهد کرد،
اگر side_effect یک پیمایشپذیر باشد، تابع ناهمگام مقدار بعدیِ پیمایشپذیر را برمیگرداند، با این حال، اگر دنبالهی نتیجه خالیشده باشد، StopAsyncIteration بلافاصله رخ میدهد،
اگر side_effect تعریفنشده باشد، تابع ناهمگام مقدار تعریفشده توسط return_value را برمیگرداند، بنابراین، بهطور پیشفرض، تابع ناهمگام یک شیء AsyncMock جدید برمیگرداند.
تنظیم spec یک Mock یا MagicMock روی یک تابع ناهمگام باعث میشود پس از فراخوانی، یک شیء همروال برگردانده شود.
اگر spec یک Mock، MagicMock یا AsyncMock را روی کلاسی دارای توابع ناهمگام و همگام تنظیم کنید، توابع همگام بهطور خودکار تشخیص داده میشوند و بهصورت MagicMock (اگر ماک والد AsyncMock یا MagicMock باشد) یا Mock (اگر ماک والد Mock باشد) تنظیم میشوند. تمام توابع ناهمگام AsyncMock خواهند بود.
این فهرستی از تمام موارد await انجامشده روی شیء ماک بهترتیب است (بنابراین طول فهرست برابر با تعداد دفعاتی است که آن شیء await شده است). پیش از انجام هرگونه await، این فهرست خالی است.
نسخهای از MagicMock برای آزمونهای چندنخی. شیء ThreadingMock متدهای اضافیای فراهم میکند تا بهجای ادعای فوری دربارهی یک فراخوانی، منتظر بمانید تا آن فراخوانی صورت گیرد.
مهلت پیشفرض توسط آرگومان timeout مشخص میشود، یا در صورت تنظیمنشدن، توسط ویژگی ThreadingMock.DEFAULT_TIMEOUT، که پیشفرض آن مسدودسازی (None) است.
اگر در زمان ایجاد ماک یک مهلت زمانی پاس داده شده باشد یا یک آرگومان مهلت زمانی به این تابع پاس داده شود، در صورتی که فراخوانی بهموقع انجام نشود، تابع استثنا AssertionError را پرتاب میکند.
اشیاء ماک فراخوانیپذیر هستند. فراخوانی، مقدار تنظیمشده بهعنوان ویژگی return_value را برمیگرداند. مقدار بازگشتی پیشفرض، یک شیء ماک جدید است؛ این شیء نخستین باری که به مقدار بازگشتی دسترسی پیدا شود (چه بهصراحت و چه با فراخوانی ماک) ایجاد میشود — اما ذخیره میشود و هر بار همان شیء برگردانده میشود.
فراخوانیهای انجامشده روی شیء در ویژگیهایی مانند call_args و call_args_list ثبت خواهند شد.
اگر side_effect تنظیمشده باشد، پس از ثبت فراخوانی، فراخوانی خواهد شد، بنابراین اگر side_effect استثنایی پرتاب کند، فراخوانی همچنان ثبت میشود.
سادهترین راه برای وادار کردن یک ماک به پرتاب یک استثنا هنگام فراخوانی، این است که side_effect را یک کلاس یا نمونه استثنا قرار دهید:
اگر side_effect یک تابع باشد، هر مقداری که آن تابع برمیگرداند همان مقداری است که فراخوانیهای ماک برمیگردانند. تابع side_effect با همان آرگومانهای ماک فراخوانی میشود. این امکان را به شما میدهد که مقدار بازگشتی فراخوانی را بهصورت پویا، بر اساس ورودی تغییر دهید:
اگر میخواهید ماک همچنان مقدار برگشتی پیشفرض (یک ماک جدید) یا هر مقدار برگشتی تعیینشده را برگرداند، دو روش برای انجام این کار وجود دارد. یا return_value را از داخل side_effect برگردانید، یا DEFAULT را برگردانید:
side_effect نیز میتواند هر شیء پیمایشپذیر باشد. فراخوانیهای مکرر به ماک مقادیر را از پیمایشپذیر برمیگردانند (تا زمانی که پیمایشپذیر خالیشده شود و یک StopIteration رخ دهد):
اشیاء ماک، ویژگیها را در صورت درخواست ایجاد میکنند. این موضوع به آنها امکان میدهد وانمود کنند که اشیایی از هر نوعی هستند.
ممکن است بخواهید یک شیء ماک در پاسخ به فراخوانی hasattr() مقدار False را برگرداند، یا هنگامی که یک ویژگی واکشی میشود، یک AttributeError پرتاب کند. میتوانید این کار را با ارائه یک شیء بهعنوان spec برای یک ماک انجام دهید، اما این روش همیشه مناسب نیست.
شما با حذف کردن ویژگیها، آنها را «مسدود» میکنید. پس از حذف، دسترسی به یک ویژگی باعث پرتاب AttributeError میشود.
از آنجا که "name" یک آرگومان برای سازندهی Mock است، اگر بخواهید شیء ماک شما دارای ویژگی "name" باشد، نمیتوانید آن را در زمان ایجاد بهسادگی به سازنده بدهید. دو جایگزین وجود دارد. یکی از گزینهها استفاده از configure_mock() است:
هنگامی که یک ماک را بهعنوان ویژگی به ماک دیگری (یا بهعنوان مقدار بازگشتی) متصل میکنید، این ماک «فرزند» آن ماک میشود. فراخوانیهای فرزند در ویژگیهای method_calls و mock_calls والد ثبت میشوند. این موضوع برای پیکربندی ماکهای فرزند و سپس اتصال آنها به والد، یا برای اتصال ماکها به والدی که تمام فراخوانیهای فرزندان را ثبت میکند و به شما امکان میدهد دربارهی ترتیب فراخوانیها بین ماکها ادعا کنید، مفید است:
استثنای این مورد زمانی است که ماک دارای نام باشد. این به شما امکان میدهد که اگر به دلیلی نمیخواهید این اتفاق بیفتد، از «والدسازی» (parenting) جلوگیری کنید.
ماکهایی که توسط patch() برای شما ایجاد میشوند، بهطور خودکار نامگذاری میشوند. برای اتصال ماکهای دارای نام به یک والد، از متد attach_mock() استفاده میکنید:
دکوراتورهای patch برای وصله کردن اشیاء فقط در محدوده تابعی که آن را دکور میکنند استفاده میشوند. آنها بهطور خودکار لغو وصله (unpatching) را برای شما مدیریت میکنند، حتی اگر استثناهایی پرتاب شوند. همچنین میتوان از همه این توابع در دستورات with یا بهعنوان دکوراتورهای کلاس استفاده کرد.
patch() بهعنوان دکوراتور تابع، دکوراتور کلاس یا مدیر زمینه عمل میکند. در بدنه تابع یا دستور with، target با یک شیء new وصله میشود. هنگامی که تابع/دستور with به پایان میرسد، وصله لغو میشود.
اگر new حذفشده باشد، چنانچه شیء وصلهشده یک تابع ناهمگام باشد، هدف با یک AsyncMock و در غیر این صورت با یک MagicMock جایگزین میشود. اگر patch() بهعنوان دکوراتور استفاده شود و new حذفشده باشد، ماک ایجادشده بهعنوان یک آرگومان اضافی به تابع دکوریتشده ارسال میشود. اگر patch() بهعنوان مدیر زمینه استفاده شود، ماک ایجادشده توسط مدیر زمینه برگردانده میشود.
target باید یک رشته در قالب 'package.module.ClassName' باشد. target ایمپورت میشود و شیء مشخصشده با شیء new جایگزین میشود، بنابراین target باید در محیطی که شما patch() را از آن فراخوانی میکنید قابل ایمپورت باشد. هدف در زمان اجرای تابع دکوریتشده ایمپورت میشود، نه در زمان اعمال دکوراتور.
اگر patch یک نمونه برای شما ایجاد کند، آرگومانهای کلیدواژهای spec و spec_set به MagicMock ارسال میشوند.
علاوه بر این، میتوانید spec=True یا spec_set=True را ارسال کنید، که باعث میشود patch شیء ماکشده را بهعنوان شیء spec/spec_set ارسال کند.
new_callable به شما امکان میدهد یک کلاس یا شیء فراخوانیپذیر متفاوت را مشخص کنید که برای ایجاد شیء new فراخوانی خواهد شد. بهطور پیشفرض برای توابع ناهمگام از AsyncMock و برای بقیه از MagicMock استفاده میشود.
شکل قدرتمندتری از مشخصات، autospec است. اگر autospec=True را تنظیم کنید، ماک با مشخصاتی از شیء جایگزینشده ایجاد میشود. همه ویژگیهای ماک نیز دارای مشخصات ویژگی متناظر از شیء جایگزینشده خواهند بود. متدها و توابعی که ماک شدهاند، آرگومانهایشان بررسی میشود و اگر با امضای نادرست فراخوانی شوند، TypeError پرتاب میکنند. برای ماکهایی که جایگزین یک کلاس میشوند، مقدار بازگشتی آنها («نمونه») همان مشخصات کلاس را خواهد داشت. تابع create_autospec() و مشخصهدهی خودکار (Autospeccing) را ببینید.
بهجای autospec=True میتوانید autospec=some_object را ارسال کنید تا بهجای شیءای که جایگزین میشود، از یک شیء دلخواه بهعنوان مشخصات استفاده کنید.
بهطور پیشفرض patch() در جایگزینی ویژگیهایی که وجود ندارند ناموفق خواهد بود. اگر create=True را ارسال کنید و ویژگی وجود نداشته باشد، patch هنگام فراخوانی تابع patchشده، آن ویژگی را برای شما ایجاد میکند و پس از پایان اجرای تابع patchشده دوباره آن را حذف میکند. این برای نوشتن آزمونهایی برای ویژگیهایی که کد تولیدی شما در رانتایم ایجاد میکند مفید است. این گزینه بهطور پیشفرض غیرفعال است، زیرا میتواند خطرناک باشد. با فعال بودن آن میتوانید آزمونهای موفقی برای APIهایی بنویسید که واقعاً وجود ندارند!
نکته
تغییر یافته در نسخهی 3.5: اگر در حال وصله کردن توکارها در یک ماژول هستید، نیازی به ارسال create=True نیست؛ این آرگومان بهصورت پیشفرض اضافه میشود.
میتوان از Patch بهعنوان دکوراتور کلاس TestCase استفاده کرد. این کار با دکوراتور کردن هر متد آزمون در کلاس انجام میشود. وقتی متدهای آزمون شما یک مجموعهی مشترک از وصلهینگها (patchings) دارند، این کار کد تکراری را کاهش میدهد. patch() با جستوجوی نام متدهایی که با patch.TEST_PREFIX شروع میشوند، آزمونها را پیدا میکند. بهطور پیشفرض این مقدار 'test' است، که با روشی که unittest آزمونها را پیدا میکند مطابقت دارد. شما میتوانید با تنظیم patch.TEST_PREFIX یک پیشوند جایگزین مشخص کنید.
میتوان از Patch بهعنوان یک مدیر زمینه، با دستور with استفاده کرد. در اینجا، وصلهکردن بر بلوک دارای تورفتگی پس از دستور with اعمال میشود. اگر از "as" استفاده کنید، شیء وصلهشده به نامی که پس از "as" میآید مقید میشود؛ اگر patch() یک شیء ماک برای شما ایجاد کند، بسیار مفید است.
patch() آرگومانهای کلیدواژهای دلخواه را میپذیرد. این آرگومانها اگر شیء وصلهشده ناهمگام باشد، به AsyncMock، در غیر این صورت به MagicMock یا در صورت مشخص بودن به new_callable ارسال میشوند.
patch.dict(...)، patch.multiple(...) و patch.object(...) برای موارد استفاده جایگزین در دسترس هستند.
patch() بهعنوان دکوراتور تابع، ماک را برای شما ایجاد میکند و آن را به تابع دکورشده میفرستد:
وصله کردن یک کلاس، آن کلاس را با یک نمونه از MagicMock جایگزین میکند. اگر در کد تحت آزمون از این کلاس نمونهسازی شود، این return_value ماک است که استفاده خواهد شد.
اگر کلاس چندین بار نمونهسازی شود، میتوانید از side_effect استفاده کنید تا هر بار یک ماک جدید برگردانده شود. بهعنوان جایگزین، میتوانید return_value را روی هر مقداری که میخواهید تنظیم کنید.
برای پیکربندی مقادیر بازگشتی متدهای نمونههایی از کلاس وصلهشده، باید این کار را روی return_value انجام دهید. برای مثال:
آرگومان new_callable زمانی مفید است که بخواهید بهجای کلاس پیشفرض MagicMock، از یک کلاس جایگزین برای ماک ایجادشده استفاده کنید. برای مثال، اگر بخواهید از NonCallableMock استفاده شود:
>>> thing=object()>>> withpatch('__main__.thing',new_callable=NonCallableMock)asmock_thing:... assertthingismock_thing... thing()...Traceback (most recent call last):...TypeError: 'NonCallableMock' object is not callable
یک مورد استفاده دیگر ممکن است جایگزینی یک شیء با یک نمونه io.StringIO باشد:
هنگامی که patch() برای شما یک ماک ایجاد میکند، معمول است که نخستین کاری که باید انجام دهید، پیکربندی ماک باشد. بخشی از این پیکربندی را میتوان در فراخوانی patch انجام داد. هر کلیدواژه دلخواهی که به فراخوانی منتقل کنید، برای تنظیم ویژگیهای ماک ایجادشده استفاده خواهد شد:
علاوه بر ویژگیهای ماک ایجادشده، میتوان ویژگیهایی مانند return_value و side_effect در ماکهای فرزند را نیز پیکربندی کرد. این موارد از نظر سینتکسی برای ارسال مستقیم بهعنوان آرگومانهای کلیدواژهای معتبر نیستند، اما همچنان میتوان یک دیکشنری با این موارد بهعنوان کلیدها را با استفاده از ** در یک فراخوانی patch() بسط داد:
بهطور پیشفرض، تلاش برای وصله کردن (patch) تابعی در یک ماژول (یا متدی یا ویژگیای در یک کلاس) که وجود ندارد، با AttributeError شکست میخورد:
>>> @patch('sys.non_existing_attribute',42)... deftest():... assertsys.non_existing_attribute==42...>>> test()Traceback (most recent call last):...AttributeError: <module 'sys' (built-in)> does not have the attribute 'non_existing_attribute'
اما افزودن create=True در فراخوانی patch() باعث میشود مثال قبلی همانطور که انتظار میرود کار کند:
عضو نامگذاریشده (attribute) روی یک شیء (target) را با یک شیء ماک وصله کنید.
patch.object() میتواند بهعنوان دکوراتور، دکوراتور کلاس یا مدیر زمینه استفاده شود. آرگومانهای new، spec، create، spec_set، autospec و new_callable همان معنایی را دارند که برای patch() دارند. مانند patch()، patch.object() آرگومانهای کلیدواژهای دلخواهی را برای پیکربندی شیء ماکی که ایجاد میکند، میپذیرد.
هنگامی که بهعنوان دکوراتور کلاس استفاده شود، patch.object() برای انتخاب متدهایی که باید پوشش داده شوند، patch.TEST_PREFIX را رعایت میکند.
میتوانید patch.object() را با سه آرگومان یا دو آرگومان فراخوانی کنید. حالت سهآرگومانی، شیء موردنظر برای وصلهشدن (patch)، نام ویژگی و شیءای که ویژگی با آن جایگزین میشود را دریافت میکند.
هنگام فراخوانی با قالب دو آرگومانی، شیء جایگزین را حذف میکنید و یک ماک برای شما ایجاد میشود و بهعنوان آرگومانی اضافی به تابع دکوریتشده ارسال میشود:
یک دیکشنری، یا شیء شبهدیکشنری را وصله (patch) کنید و پس از آزمون، دیکشنری را به حالت اصلی خود بازگردانید؛ بهطوریکه دیکشنری بازیابیشده، نسخهای از دیکشنری همانگونه است که پیش از آزمون بوده است.
in_dict میتواند یک دیکشنری یا یک ظرف شبهنگاشت باشد. اگر یک نگاشت باشد، باید دستکم از دریافت، تنظیم و حذف آیتمها بههمراه تکرار روی کلیدها پشتیبانی کند.
in_dict همچنین میتواند رشتهای باشد که نام دیکشنری را مشخص میکند، که در این صورت با ایمپورت کردن آن واکشی میشود.
values میتواند یک دیکشنری از مقادیر برای تنظیم در دیکشنری باشد. values همچنین میتواند یک پیمایشپذیر از جفتهای (key,value) باشد.
اگر clear برابر با true باشد، دیکشنری پیش از تنظیم مقادیر جدید پاک میشود.
patch.dict() را همچنین میتوان با آرگومانهای کلیدواژهای دلخواه برای تنظیم مقادیر در دیکشنری فراخوانی کرد.
تغییر یافته در نسخهی 3.8: patch.dict() اکنون هنگامی که بهعنوان مدیر زمینه استفاده شود، دیکشنری وصلهشده را بازمیگرداند.
patch.dict() میتواند بهعنوان مدیر زمینه، دکوراتور یا دکوراتور کلاس استفاده شود:
هنگام استفاده بهعنوان دکوراتور کلاس، patch.dict() برای انتخاب متدهایی که باید پوشانده شوند، patch.TEST_PREFIX را (با مقدار پیشفرض 'test') رعایت میکند:
اگر میخواهید برای آزمون خود از پیشوند متفاوتی استفاده کنید، میتوانید با تنظیم patch.TEST_PREFIX، وصلهکنندهها (patchers) را از پیشوند متفاوت مطلع سازید. برای جزئیات بیشتر دربارهی نحوهی تغییر مقدار، TEST_PREFIX را ببینید.
patch.dict() میتواند برای افزودن اعضا به یک دیکشنری به کار رود، یا بهسادگی به یک آزمون اجازه دهد تا یک دیکشنری را تغییر دهد، و اطمینان حاصل کند که دیکشنری پس از پایان آزمون بازیابی میشود.
>>> foo={}>>> withpatch.dict(foo,{'newkey':'newvalue'})aspatched_foo:... assertfoo=={'newkey':'newvalue'}... assertpatched_foo=={'newkey':'newvalue'}... # You can add, update or delete keys of foo (or patched_foo, it's the same dict)... patched_foo['spam']='eggs'...>>> assertfoo=={}>>> assertpatched_foo=={}
میتوان از patch.dict() با اشیایی شبیه به دیکشنری استفاده کرد که در واقع دیکشنری نیستند. در حداقل حالت، آنها باید از دریافت، تنظیم و حذف آیتمها و نیز تکرار یا آزمون عضویت پشتیبانی کنند. این موضوع متناظر با متدهای جادویی __getitem__()، __setitem__()، __delitem__() و نیز __iter__() یا __contains__() است.
چندین وصله (patch) را در یک فراخوانی اعمال میکند. شیء مورد نظر برای وصله شدن (چه بهصورت شیء و چه بهصورت رشتهای برای واکشی شیء از طریق ایمپورت) و آرگومانهای کلیدواژهای برای وصلهها را دریافت میکند:
اگر میخواهید patch.multiple() ماکها را برای شما ایجاد کند، از DEFAULT بهعنوان مقدار استفاده کنید. در این حالت، ماکهای ایجادشده بهصورت آرگومانهای کلیدواژهای به یک تابع دکوریتشده پاس داده میشوند، و هنگامی که از patch.multiple() بهعنوان یک مدیر زمینه استفاده شود، یک دیکشنری برگردانده میشود.
patch.multiple() را میتوان بهعنوان دکوراتور، دکوراتور کلاس یا مدیر زمینه استفاده کرد. آرگومانهای spec، spec_set، create، autospec و new_callable همان معنایی را دارند که در patch() دارند. این آرگومانها بر همهی وصلههایی که بهوسیلهی patch.multiple() انجام میشوند، اعمال خواهند شد.
هنگامی که بهعنوان دکوراتور کلاس استفاده شود، patch.multiple() مقدار patch.TEST_PREFIX را برای انتخاب متدهایی که باید پوشانده شوند رعایت میکند.
اگر میخواهید patch.multiple() ماکها را برای شما ایجاد کند، آنگاه میتوانید از DEFAULT بهعنوان مقدار استفاده کنید. اگر از patch.multiple() بهعنوان آراینده استفاده کنید، ماکهای ایجادشده با کلیدواژه به تابع آراستهشده ارسال میشوند.
patch.multiple() میتواند با سایر دکوراتورهای patch تودرتو شود، اما آرگومانهای کلیدواژهای را پس از هر یک از آرگومانهای استانداردی که بهوسیله patch() ایجاد میشوند قرار دهید:
اگر patch.multiple() بهعنوان یک مدیر زمینه استفاده شود، مقدار بازگشتی از مدیر زمینه یک دیکشنری است که در آن ماکهای ایجادشده با نام بهعنوان کلید قرار دارند:
همهی وصلهکنندهها (patchers) متدهای start() و stop() را دارند. این متدها وصلهکردن در متدهای setUp یا هر جا که میخواهید چندین وصله را بدون تودرتو کردن دکوراتورها یا دستورهای with انجام دهید، سادهتر میکنند.
برای استفاده از آنها، patch()، patch.object() یا patch.dict() را بهصورت عادی فراخوانی کنید و یک ارجاع به شیء patcher برگرداندهشده نگه دارید. سپس میتوانید start() را برای اعمال وصله (patch) و stop() را برای لغو آن فراخوانی کنید.
اگر از patch() برای ایجاد یک ماک برای خودتان استفاده میکنید، آن با فراخوانی patcher.start برگردانده میشود.
اگر از این روش استفاده کنید، باید اطمینان حاصل کنید که وصلهکردن با فراخوانی stop «لغو» میشود. این کار ممکن است پیچیدهتر از آن باشد که تصور میکنید، زیرا اگر در setUp استثنایی پرتاب شود، tearDown فراخوانی نمیشود. unittest.TestCase.addCleanup() این کار را آسانتر میکند:
میتوان از همهی وصلهکنندهها (patchers) بهعنوان دکوراتور کلاس استفاده کرد. وقتی به این شکل استفاده شوند، هر متد آزمون در کلاس را میپوشانند. وصلهکنندهها متدهایی را که با 'test' آغاز میشوند، بهعنوان متد آزمون میشناسند. این همان روشی است که unittest.TestLoader بهطور پیشفرض متدهای آزمون را پیدا میکند.
ممکن است بخواهید برای آزمونهایتان از پیشوند متفاوتی استفاده کنید. میتوانید با تنظیم patch.TEST_PREFIX، وصلهکنندهها را از پیشوند متفاوت مطلع کنید:
توجه داشته باشید که دکوراتورها از پایین به بالا اعمال میشوند. این روش استاندارد پایتون برای اعمال دکوراتورها است. ترتیب ماکهای ایجادشدهای که به تابع آزمون شما ارسال میشوند، با این ترتیب مطابقت دارد.
patch() با تغییر دادن (بهطور موقت) شیءای که یک نام به آن اشاره میکند به شیءای دیگر کار میکند. ممکن است نامهای زیادی به یک شیء واحد اشاره کنند، بنابراین برای اینکه وصله کردن کار کند، باید اطمینان حاصل کنید که نامی را وصله میکنید که سیستم تحت آزمون از آن استفاده میکند.
اصل اساسی این است که شما محلی را که یک شیء در آن جستجو میشود وصله (patch) میکنید، که لزوماً همان محلی نیست که در آن تعریفشده است. چند مثال به روشنشدن این موضوع کمک میکند.
فرض کنید پروژهای داریم که میخواهیم آن را با ساختار زیر آزمون کنیم:
اکنون میخواهیم some_function را آزمون کنیم، اما میخواهیم با استفاده از patch()، SomeClass را ماک کنیم. مشکل این است که وقتی ماژول b را ایمپورت میکنیم (که باید این کار را انجام دهیم)، این ماژول SomeClass را از ماژول a ایمپورت میکند. اگر از patch() برای ماک کردن a.SomeClass استفاده کنیم، تأثیری بر آزمون ما نخواهد داشت؛ ماژول b از قبل ارجاعی به SomeClassواقعی دارد و به نظر میرسد وصله کردن ما بیاثر بوده است.
نکتهی کلیدی این است که SomeClass را در جایی که استفاده میشود (یا جایی که جستوجو میشود) با وصله (patch) جایگزین کنید. در این حالت، some_function در واقع SomeClass را در ماژول b جستوجو میکند، جایی که آن را ایمپورت کردهایم. وصله (patch) باید به این شکل باشد:
@patch('b.SomeClass')
با این حال، حالت جایگزینی را در نظر بگیرید که در آن بهجای fromaimportSomeClass، ماژول b importa را اجرا میکند و some_function از a.SomeClass استفاده میکند. هر دوی این روشهای ایمپورت رایج هستند. در این حالت، کلاسی که میخواهیم وصله (patch) کنیم، در ماژول جستجو میشود؛ بنابراین باید بهجای آن a.SomeClass را وصله (patch) کنیم:
هر دو patch و patch.object توصیفگرها را بهدرستی وصله و بازیابی میکنند: متدهای کلاس، متدهای ایستا و پراپرتیها. شما باید اینها را بهجای یک نمونه، روی کلاس وصله کنید. آنها همچنین با برخی اشیایی که دسترسی به ویژگیها را پراکسی میکنند، مانند شیء تنظیمات Django نیز کار میکنند.
Mock از ماک کردن متدهای پروتکل پایتون، که با نام "متدهای جادویی" نیز شناخته میشوند، پشتیبانی میکند. این کار به اشیاء ماک اجازه میدهد جایگزین ظرفها یا اشیاء دیگری شوند که پروتکلهای پایتون را پیادهسازی میکنند.
از آنجا که متدهای جادویی بهشکل متفاوتی نسبت به متدهای عادی جستجو میشوند [2]، این پشتیبانی بهطور جادویی پیادهسازی شده است. این بدان معناست که تنها متدهای جادویی خاصی پشتیبانی میشوند. فهرست پشتیبانیشوده شامل تقریباً همهی آنها میشود. اگر موردی وجود ندارد که به آن نیاز دارید، لطفاً به ما اطلاع دهید.
شما متدهای جادویی را با تنظیم متد مورد نظر خود به یک تابع یا یک نمونه ماک، ماک میکنید. اگر از یک تابع استفاده میکنید، آن تابع بایدself را بهعنوان اولین آرگومان دریافت کند [3].
فراخوانیهای متدهای جادویی در method_calls ظاهر نمیشوند، اما در mock_calls ثبت میشوند.
نکته
اگر برای ایجاد یک ماک از آرگومان کلیدواژهای spec استفاده کنید، تلاش برای تنظیم یک متد جادویی که در spec وجود ندارد، باعث پرتاب استثنای AttributeError میشود.
فهرست کامل متدهای جادویی پشتیبانیشده عبارت است از:
__hash__، __sizeof__، __repr__ و __str__
__dir__، __format__ و __subclasses__
__round__، __floor__، __trunc__ و __ceil__
مقایسهها: __lt__، __gt__، __le__، __ge__، __eq__ و __ne__
تغییر یافته در نسخهی 3.8: پشتیبانی از __aenter__، __aexit__، __aiter__ و __anext__ اضافه شد.
متدهای زیر وجود دارند اما پشتیبانی نمیشوند، زیرا یا توسط ماک استفاده میشوند، یا نمیتوان آنها را بهصورت پویا تنظیم کرد، یا ممکن است مشکلاتی ایجاد کنند:
MagicMock زیرکلاسی از Mock است که پیادهسازیهای پیشفرضی برای بیشتر متدهای جادویی دارد. میتوانید بدون اینکه خودتان متدهای جادویی را پیکربندی کنید، از MagicMock استفاده کنید.
پارامترهای سازنده همان معنای پارامترهای Mock را دارند.
اگر از آرگومانهای spec یا spec_set استفاده کنید، فقط متدهای جادویی که در spec وجود دارند ایجاد میشوند.
بهطور پیشفرض، بسیاری از متدهای پروتکل باید اشیایی از یک نوع مشخص را برگردانند. این متدها از پیش با یک مقدار بازگشتی پیشفرض پیکربندی شدهاند، بهطوری که اگر به مقدار بازگشتی علاقهای ندارید، میتوانید بدون نیاز به انجام هیچ کاری از آنها استفاده کنید. اگر بخواهید مقدار پیشفرض را تغییر دهید، همچنان میتوانید مقدار بازگشتی را بهصورت دستی تنظیم کنید.
دو متد برابری، __eq__() و __ne__()، خاص هستند. آنها مقایسه برابری پیشفرض بر اساس هویت را با استفاده از ویژگی side_effect انجام میدهند، مگر اینکه مقدار بازگشتی آنها را تغییر دهید تا چیز دیگری برگردانند:
MagicMock تمام متدهای جادویی پشتیبانیشده را بهصورت پیکربندیشده دارد، بهجز برخی از موارد مبهم و منسوخ. همچنان در صورت تمایل میتوانید آنها را پیکربندی کنید.
متدهای جادویی که پشتیبانی میشوند اما بهطور پیشفرض در MagicMock تنظیم نشدهاند:
__subclasses__
__dir__
__format__
__get__، __set__ و __delete__
__reversed__ و __missing__
__reduce__، __reduce_ex__، __getinitargs__، __getnewargs__، __getstate__ و __setstate__
شیء sentinel روشی مناسب برای فراهم کردن اشیای یکتا برای آزمونهایتان فراهم میکند.
ویژگیها بهصورت درخواستی، هنگامی که با نام به آنها دسترسی پیدا میکنید، ایجاد میشوند. دسترسی به یک ویژگی یکسان همیشه یک شیء یکسان را برمیگرداند. اشیاء برگرداندهشده repr مناسبی دارند تا پیامهای شکست آزمون خوانا باشند.
تغییر یافته در نسخهی 3.7: ویژگیهای sentinel اکنون هویت خود را هنگامی که کپی یا پیکل میشوند، حفظ میکنند.
گاهی هنگام آزمون، نیاز دارید بررسی کنید که یک شیء خاص بهعنوان آرگومان به متد دیگری ارسال میشود یا برگردانده میشود. ایجاد اشیای sentinel نامدار برای آزمون این موضوع میتواند رایج باشد. sentinel روش مناسبی برای ایجاد و آزمون هویت اشیایی مانند این فراهم میکند.
در این مثال، method را مانکیوصله (monkey patch) میکنیم تا sentinel.some_object را برگرداند:
شیء DEFAULT یک نشانگر از پیش ساختهشده (sentinel) است (در واقع sentinel.DEFAULT). توابع side_effect میتوانند از آن برای نشان دادن اینکه باید از مقدار بازگشتی عادی استفاده شود، استفاده کنند.
برای یک شیء فراخوانی که نشاندهنده چندین فراخوانی است، call_list() فهرستی از همه فراخوانیهای میانی و همچنین فراخوانی نهایی را برمیگرداند.
call_list بهویژه برای نوشتن ادعاها در مورد «فراخوانیهای زنجیرهای» مفید است. یک فراخوانی زنجیرهای شامل چندین فراخوانی در یک خط کد است. این موضوع باعث ایجاد چندین ورودی در mock_calls روی یک ماک میشود. ساخت دستی دنبالهی فراخوانیها میتواند خستهکننده باشد.
call_list() میتواند دنباله فراخوانیها را از همان فراخوانی زنجیرهای بسازد:
یک شیء call بسته به چگونگی ساخت آن، یا یک تاپل از (آرگومانهای جایگاهی، آرگومانهای کلیدواژهای) است یا (نام، آرگومانهای جایگاهی، آرگومانهای کلیدواژهای). وقتی خودتان آنها را میسازید، این موضوع چندان جالب نیست، اما میتوان اشیاء call موجود در ویژگیهای Mock.call_args، Mock.call_args_list و Mock.mock_calls را دروننگری کرد تا به آرگومانهای منفردی که در بر دارند، دست یافت.
اشیای call در Mock.call_args و Mock.call_args_list تاپلهای دوتایی از (آرگومانهای جایگاهی، آرگومانهای کلیدواژهای) هستند، در حالی که اشیای call در Mock.mock_calls، به همراه آنهایی که خودتان میسازید، تاپلهای سهتایی از (نام، آرگومانهای جایگاهی، آرگومانهای کلیدواژهای) هستند.
میتوانید از «تاپلبودن» آنها برای استخراج تکتک آرگومانها جهت دروننگری و ادعاهای پیچیدهتر استفاده کنید. آرگومانهای جایگاهی یک تاپل هستند (اگر آرگومان جایگاهی وجود نداشته باشد، یک تاپل خالی هستند) و آرگومانهای کلیدواژهای یک دیکشنری هستند:
یک شیء ماک را با استفاده از شیء دیگری بهعنوان مشخصه ایجاد کنید. ویژگیهای ماک از ویژگی متناظر در شیء spec بهعنوان مشخصه خود استفاده خواهند کرد.
آرگومانهای توابع یا متدهای ماکشده بررسی خواهند شد تا اطمینان حاصل شود که آنها با امضای صحیح فراخوانی میشوند.
اگر spec_set برابر True باشد، تلاش برای تنظیم ویژگیهایی که در شیء spec وجود ندارند، باعث پرتاب AttributeError میشود.
اگر از یک کلاس بهعنوان مشخصات استفاده شود، مقدار بازگشتی ماک (نمونهی کلاس) همان مشخصات را خواهد داشت. میتوانید با ارسال instance=True از یک کلاس بهعنوان مشخصات برای یک شیء نمونه استفاده کنید. ماک برگرداندهشده تنها در صورتی فراخوانیپذیر خواهد بود که نمونههای ماک فراخوانیپذیر باشند.
create_autospec() همچنین آرگومانهای کلیدواژهای دلخواه را میپذیرد که به سازندهی ماک ایجادشده ارسال میشوند.
گاهی ممکن است نیاز داشته باشید دربارهی برخی از آرگومانها در یک فراخوانی ماک ادعا کنید، اما یا برخی از آرگومانها برایتان مهم نباشد یا بخواهید آنها را بهصورت جداگانه از call_args استخراج کنید و ادعاهای پیچیدهتری دربارهی آنها مطرح کنید.
برای نادیده گرفتن برخی آرگومانها، میتوانید اشیایی را ارسال کنید که در مقایسه با همهچیز برابر هستند. در این صورت، فراخوانیهای assert_called_with() و assert_called_once_with() بدون توجه به اینکه چه چیزی ارسال شده باشد، موفق خواهند بود.
FILTER_DIR متغیری در سطح ماژول است که نحوه پاسخ اشیای ماک به dir() را کنترل میکند. مقدار پیشفرض True است که از فیلتری که در زیر توضیح داده شده است استفاده میکند تا تنها اعضای مفید را نمایش دهد. اگر این فیلتر را نمیپسندید یا نیاز دارید آن را برای اهداف عیبیابی غیرفعال کنید، سپس mock.FILTER_DIR=False را تنظیم کنید.
با فعال بودن فیلتر، dir(some_mock) فقط ویژگیهای مفید را نشان میدهد و ویژگیهایی را که بهصورت پویا ایجاد شدهاند و بهطور معمول نمایش داده نمیشوند، شامل میشود. اگر ماک با یک spec (یا البته autospec) ایجاد شده باشد، تمام ویژگیهای شیء اصلی نمایش داده میشوند، حتی اگر هنوز به آنها دسترسی پیدا نشده باشد:
بسیاری از ویژگیهای نهچندان مفید (خصوصی برای Mock بهجای شیء ماکشده) که پیشوند زیرخط و زیرخط دوتایی دارند، از نتیجهی فراخوانی dir() روی یک Mock حذف شدهاند. اگر این رفتار را نمیپسندید، میتوانید با تنظیم کلید سطح ماژول FILTER_DIR آن را غیرفعال کنید:
بهعنوان جایگزین، میتوانید صرفاً از vars(my_mock) (اعضای نمونه) و dir(type(my_mock)) (اعضای نوع) برای دور زدن فیلترکردن، صرفنظر از FILTER_DIR، استفاده کنید.
یک تابع کمکی برای ایجاد یک ماک جهت جایگزینی استفاده از open(). این تابع برای open()، چه مستقیماً فراخوانی شود و چه بهعنوان مدیر زمینه استفاده شود، کار میکند.
آرگومان mock، شیء ماک برای پیکربندی است. اگر None (پیشفرض) باشد، یک MagicMock برای شما ایجاد میشود که API آن به متدها یا ویژگیهای موجود در دستههای پرونده استاندارد محدود است.
read_data رشتهای است که متدهای read()، readline() و readlines() دسته پرونده باید آن را برگردانند. فراخوانیهای آن متدها داده را از read_data میگیرند تا زمانی که تمام شود. ماک این متدها نسبتاً ساده است: هر بار که ماک فراخوانی میشود، read_data به ابتدا بازمیگردد. اگر به کنترل بیشتری بر دادهای نیاز دارید که به کد آزمایششده میدهید، باید این ماک را خودتان سفارشی کنید. وقتی این کافی نباشد، یکی از بستههای سامانه فایلبندی درونحافظهای در PyPI میتواند یک سامانه فایلبندی واقعگرایانه برای آزمایش ارائه دهد.
تغییر یافته در نسخهی 3.4: پشتیبانی از readline() و readlines() افزوده شد. ماکِ read() تغییر کرد تا بهجای برگرداندن read_data در هر فراخوانی، آن را مصرف کند.
تغییر یافته در نسخهی 3.5: اکنون read_data در هر فراخوانی به ماک بازنشانی میشود.
تغییر یافته در نسخهی 3.8: __iter__() به پیادهسازی افزوده شد تا تکرار (مانند حلقههای for) read_data را بهدرستی مصرف کند.
استفاده از open() بهعنوان مدیر زمینه، روشی عالی برای اطمینان از بسته شدن صحیح دستههای پرونده است و در حال رایج شدن است:
مشکل این است که حتی اگر فراخوانی open() را ماک کنید، این شیء برگرداندهشده است که بهعنوان مدیر زمینه استفاده میشود (و __enter__() و __exit__() فراخوانی میشوند).
ماک کردن مدیرهای زمینه با یک MagicMock بهقدری رایج و پردردسر است که یک تابع کمکی مفید واقع میشود.
مشخصهدهی خودکار (Autospeccing) مبتنی بر ویژگی spec موجود در ماک است. این قابلیت، API ماکها را به API شیء اصلی محدود میکند، اما این محدودسازی بهصورت بازگشتی (و با پیادهسازی تنبلانه) انجام میشود، بهطوری که ویژگیهای ماکها تنها همان API ویژگیهای spec را دارند. علاوه بر این، توابع / متدهای ماکشده همان امضای فراخوانی نسخه اصلی را دارند، بنابراین اگر بهصورت نادرست فراخوانی شوند، یک TypeError پرتاب میکنند.
پیش از آنکه توضیح دهم که مشخصهسازی خودکار (auto-speccing) چگونه کار میکند، دلیل نیاز به آن در اینجا آمده است.
Mock یک شیء بسیار قدرتمند و انعطافپذیر است، اما دچار یک نقص است که در ماکسازی بهطور کلی وجود دارد. اگر بخشی از کد خود را بازنویسی کنید، نام اعضا را تغییر دهید و غیره، هر آزمونی برای کدی که هنوز از API قدیمی استفاده میکند، اما بهجای اشیاء واقعی از ماکها استفاده میکند، همچنان موفق میشود. این بدان معناست که حتی اگر کد شما خراب باشد، همهی آزمونهای شما میتوانند موفق شوند.
تغییر یافته در نسخهی 3.5: پیش از 3.5، آزمونهایی که در کلمه assert غلط املایی داشتند، در حالی که باید خطایی پرتاب میکردند، بیصدا قبول میشدند. هنوز هم میتوانید با ارسال unsafe=True به Mock این رفتار را داشته باشید.
توجه داشته باشید که این دلیل دیگری است که چرا به آزمونهای یکپارچگی و همچنین آزمون واحدها نیاز دارید. آزمون کردن همهچیز بهصورت جداگانه ممکن است کاملاً خوب باشد، اما اگر نحوهی «بههم متصل شدن» واحدهای خود را آزمون نکنید، هنوز جای زیادی برای باگهایی وجود دارد که آزمونها میتوانستند آنها را شناسایی کنند.
unittest.mock از قبل قابلیتی برای کمک به این موضوع ارائه میدهد که speccing نامیده میشود. اگر از یک کلاس یا نمونه بهعنوان spec برای یک ماک استفاده کنید، تنها میتوانید به ویژگیهایی از ماک دسترسی داشته باشید که در کلاس واقعی وجود دارند:
>>> fromurllibimportrequest>>> mock=Mock(spec=request.Request)>>> mock.assret_called_with# Intentional typo!Traceback (most recent call last):...AttributeError: Mock object has no attribute 'assret_called_with'
spec فقط به خود ماک اعمال میشود، بنابراین همچنان همان مشکل را با هر یک از متدهای ماک داریم:
>>> mock.header_items()<mock.Mock object at 0x...>>>> mock.header_items.assret_called_with()# Intentional typo!
این مشکل با specدهی خودکار (auto-speccing) حل میشود. شما میتوانید autospec=True را به patch() / patch.object() ارسال کنید یا از تابع create_autospec() برای ایجاد یک ماک با spec استفاده کنید. اگر از آرگومان autospec=True برای patch() استفاده کنید، شیءای که جایگزین میشود بهعنوان شیء spec استفاده خواهد شد. از آنجا که specدهی (speccing) بهصورت «تنبل» (lazily) انجام میشود (spec در حین دسترسی به ویژگیهای ماک ایجاد میشود)، میتوانید از آن با اشیاء بسیار پیچیده یا عمیقاً تودرتو (مانند ماژولهایی که ماژولهایی را ایمپورت میکنند که آنها ماژولهایی را ایمپورت میکنند) بدون افت عملکرد قابلتوجه استفاده کنید.
میتوانید ببینید که request.Request دارای مشخصات است. request.Request در سازنده دو آرگومان میگیرد (که یکی از آنها self است). اگر سعی کنیم آن را بهصورت نادرست فراخوانی کنیم، این اتفاق میافتد:
>>> req=request.Request()Traceback (most recent call last):...TypeError: <lambda>() takes at least 2 arguments (1 given)
spec همچنین به کلاسهای نمونهسازیشده نیز اعمال میشود (یعنی مقدار بازگشتی ماکهای دارای spec):
اشیای Request فراخوانیپذیر نیستند، بنابراین مقدار بازگشتی نمونهسازی request.Request ماکشدهی ما یک ماک غیرفراخوانیپذیر است. با تنظیم مشخصات، هرگونه خطای تایپی در assertهای ما خطای صحیح را پرتاب خواهد کرد:
>>> req.add_header('spam','eggs')<MagicMock name='request.Request().add_header()' id='...'>>>> req.add_header.assret_called_with# Intentional typo!Traceback (most recent call last):...AttributeError: Mock object has no attribute 'assret_called_with'>>> req.add_header.assert_called_with('spam','eggs')
در بسیاری از موارد، بهسادگی میتوانید autospec=True را به فراخوانیهای موجود patch() خود اضافه کنید و سپس در برابر خطاهای ناشی از اشتباهات تایپی و تغییرات API محافظت شوید.
علاوه بر استفاده از autospec از طریق patch()، create_autospec() نیز برای ایجاد ماکهای autospecشده بهصورت مستقیم وجود دارد:
البته این مورد بدون ملاحظات و محدودیتها نیست؛ به همین دلیل، رفتار پیشفرض نیست. برای اینکه بدانید چه ویژگیهایی در شیء spec در دسترس هستند، autospec باید spec را دروننگری (دسترسی به ویژگیها) کند. هنگامی که ویژگیهای ماک را پیمایش میکنید، پیمایش متناظری از شیء اصلی در پشت صحنه در حال انجام است. اگر هر یک از اشیاء دارای spec شما پراپرتیها یا توصیفگرهایی داشته باشند که بتوانند باعث اجرای کد شوند، ممکن است نتوانید از autospec استفاده کنید. از سوی دیگر، بسیار بهتر است اشیاء خود را بهگونهای طراحی کنید که دروننگری بیخطر باشد [4].
یک مشکل جدیتر این است که معمولاً ویژگیهای نمونه در متد __init__() ایجاد میشوند و اصلاً در کلاس وجود ندارند. autospec نمیتواند از هیچیک از ویژگیهایی که بهصورت پویا ایجاد میشوند آگاه باشد و API را به ویژگیهای قابلمشاهده محدود میکند.
>>> classSomething:... def__init__(self):... self.a=33...>>> withpatch('__main__.Something',autospec=True):... thing=Something()... thing.a...Traceback (most recent call last):...AttributeError: Mock object has no attribute 'a'
چند روش مختلف برای حل این مشکل وجود دارد. سادهترین، اما نه لزوماً کمآزارترین، روش این است که پس از ایجاد ماک، ویژگیهای مورد نیاز را بهسادگی روی آن تنظیم کنید. صرفاً به این دلیل که autospec به شما اجازه نمیدهد ویژگیهایی را که در مشخصه وجود ندارند دریافت کنید، مانع از تنظیم آنها نمیشود:
نسخهای سختگیرانهتر از هر دو spec و autospec وجود دارد که قطعاً شما را از تنظیم ویژگیهای ناموجود باز میدارد. این حالت زمانی مفید است که بخواهید کد شما نیز فقط ویژگیهای معتبر را تنظیم کند، اما بدیهی است که مانع این سناریوی خاص میشود:
>>> withpatch('__main__.Something',autospec=True,spec_set=True):... thing=Something()... thing.a=33...Traceback (most recent call last):...AttributeError: Mock object has no attribute 'a'
احتمالاً بهترین راه برای حل این مسئله، افزودن ویژگیهای کلاس بهعنوان مقادیر پیشفرض برای اعضای نمونهای است که در __init__() مقداردهی اولیه میشوند. توجه داشته باشید که اگر فقط ویژگیهای پیشفرض را در __init__() تنظیم میکنید، فراهم کردن آنها از طریق ویژگیهای کلاس (که البته بین نمونهها مشترک هستند) سریعتر نیز خواهد بود. برای مثال
classSomething:a=33
این موضوع، مسئله دیگری را پیش میآورد. نسبتاً رایج است که مقدار پیشفرض None برای اعضایی که بعداً شیءای از نوع دیگر خواهند بود، ارائه شود. None بهعنوان یک مشخصه بیفایده خواهد بود، زیرا به شما اجازه نمیدهد به هیچ ویژگی یا متدی از آن دسترسی داشته باشید. از آنجا که Noneهرگز بهعنوان یک مشخصه مفید نخواهد بود، و احتمالاً نشاندهنده عضوی است که معمولاً از نوع دیگری خواهد بود، autospec برای اعضایی که روی None تنظیم شدهاند از مشخصه استفاده نمیکند. اینها فقط ماکهای معمولی خواهند بود (البته - MagicMocks):
اگر تغییر کلاسهای محیط تولید برای افزودن مقادیر پیشفرض مورد پسند شما نیست، گزینههای بیشتری وجود دارد. یکی از این گزینهها این است که بهسادگی از یک نمونه بهعنوان مشخصه بهجای کلاس استفاده کنید. گزینه دیگر این است که یک کلاس فرعی از کلاس محیط تولید بسازید و مقادیر پیشفرض را به کلاس فرعی اضافه کنید، بدون آنکه کلاس محیط تولید تحت تأثیر قرار گیرد. هر دوی این موارد مستلزم آن است که از یک شیء جایگزین بهعنوان مشخصه استفاده کنید. خوشبختانه patch() از این حالت پشتیبانی میکند؛ میتوانید بهسادگی شیء جایگزین را بهعنوان آرگومان autospec ارسال کنید:
Seal ایجاد خودکار ماکها را هنگام دسترسی به ویژگیای از ماکِ در حال مهر و موم شدن یا هر یک از ویژگیهای آن که از قبل ماک هستند، بهصورت بازگشتی غیرفعال میکند.
اگر یک نمونه ماک دارای نام یا مشخصات به یک ویژگی انتساب داده شود، در زنجیره مهر و موم (sealing) در نظر گرفته نمیشود. این کار به شما اجازه میدهد از تثبیت بخشی از شیء ماک توسط seal جلوگیری کنید.
>>> mock=Mock()>>> mock.submock.attribute1=2>>> mock.not_submock=mock.Mock(name="sample_name")>>> seal(mock)>>> mock.new_attribute# This will raise AttributeError.>>> mock.submock.attribute2# This will raise AttributeError.>>> mock.not_submock.attribute2# This won't raise.
اگر هر ۳ مورد تنظیم شده باشند، ماک مقدار را از side_effect برمیگرداند و return_value و شیء دربرگرفتهشده را بهکلی نادیده میگیرد. اگر هر ۲ مورد از آنها تنظیم شده باشند، موردی که اولویت بالاتری دارد مقدار را برمیگرداند. صرفنظر از اینکه کدامیک ابتدا تنظیم شده باشد، ترتیب اولویت بدون تغییر باقی میماند.
از آنجا که None مقدار پیشفرض side_effect است، اگر مقدار آن را دوباره به None انتساب دهید، ترتیب اولویت بین return_value و شیء پوششدادهشده بررسی خواهد شد و side_effect نادیده گرفته میشود.
ترتیب اولویت، این مقدار را نادیده میگیرد و به آخرین جانشین، یعنی شیء پوششدادهشده، منتقل میشود.
از آنجا که فراخوانی واقعی به شیء پوشیدهشده انجام میشود، ایجاد یک نمونه از این ماک، نمونهی واقعی کلاس را برمیگرداند. آرگومانهای جایگاهی مورد نیاز شیء پوشیدهشده، در صورت وجود، باید ارسال شوند.
آخرین بهروزرسانی در نوامبر 12, 2026 (06:19 UTC).
آیا اشکالی یافتید؟
ساخته شده با Sphinx 9.1.0.
You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.