در هر برنامه ممکن است قسمتهایی وجود داشته باشند که برای عملکرد خود به API بیرونی نیاز دارند. مثلا فرض کنید قسمتی از برنامه به بات تلگرام وصل شده و پیامی را میفرستد، شما نباید این API را در تست خود داشته باشید زیرا هر بار که تست خود را اجرا میکنید اولا نیاز به اینترنت دارید و همچنین باید مقدار زمانی که طول میکشد تا سرور تلگرام جواب شما را بدهد برای پاس شدن تست معطل بمانید که هر دوی اینها منطقی نیست.
برای اینکار کتابخانه Mock
در پایتون به شما امکان میدهد تا با استفاده از آن رفتار شی ای دیگر را شبیه سازی کنید. برای استفاده از این کتابخانه آنرا بوسیله pip install mock
نصب کنید. حال با چیزی شبیه Python Shell
یا ipython
دستور زیر را راجرا کنید تا کمی بیشتر با عملکرد آن آشنا شوید.
from unittest import mock m = mock.Mock()
تا اینجا یک نمونه از Mock
ساختیم و برای اینکه بدانیم این شی چه امکاناتی به ما میدهد:
dir(m)
و خروجی کل attribute
های این شی است:
['assert_any_call', 'assert_called_once_with', 'assert_called_with', 'assert_has_calls', 'attach_mock', 'call_args', 'call_args_list', 'call_count', 'called', 'configure_mock', 'method_calls', 'mock_add_spec', 'mock_calls', 'reset_mock', 'return_value', 'side_effect']
ولی امکان جالب و دور از انتظار این است که شما میتوانید از attribute
هایی که برای این شی تعریف نشده هم استفاده کنید و خروجی همان قبلی خواهد بود:
dir(m.some_attribute)
عموما برای تست ها میخواهیم بدانیم متد مورد نظر صدا زده میشود(call) یا خیر. مثلا همینکه بدانیم کلاس Telegram متدی به نام Send دارد و آن نیز در اینجا صدا زده شده کافی است. برای اینکار از خاصیت assert_called_with میتوانید استفاده کنید. اما برای شروع میتوانیم تستی بنویسیم که متد مورد نظر یک خروجی شبیه چیزی که تلگرام به ما میدهد را برگرداند.
from unittest import TestCase from unittest.mock import Mock class TestTelegram(TestCase): def test_send_telegram_message(self): MockTelegram = Mock() MockTelegram.send.return_value = [ { 'id': 100121, 'text': 'Test Title' } ] response = MockTelegram.send() self.assertIsNotNone(response) self.assertIsInstance(response[0], dict)
تا اینجا متدی از تلگرام که حتی آنرا ننوشته ایم را تست میکنیم که متد send
آن دارای خروجی شبیه return_value
است. شما احتمالا میخواهید بدانید اگر سرور تلگرام در واقعیت جواب نمیداد و سیستم ما به آن دسترسی نداشت را چگونه تست کنیم. برای این کار از side_effect
استفاده کنید.
from unittest import TestCase from unittest.mock import Mock class TestTelegram(TestCase): def test_does_not_send_telegram_message(self): MockBlog = Mock() MockBlog.send.side_effect = Exception('Telegram Unreachable') with self.assertRaises(Exception): MockBlog.send()
در این تست در واقع میخواهیم منتظر باشیم تا اگر تلگرام پاسخ نداد متد send
اکسپشن بدهد.