Mocks و Fixtures
Mocks & Fixtures
Mocks و Fixtures — Mocks & Fixtures
كودك في الواقع لا يعيش وحيداً. يتصل بقواعد بيانات، يُرسل طلبات HTTP، يقرأ ملفات، يستخدم ساعة النظام. هذه التبعيات الخارجية (External Dependencies) تُصعّب الاختبار: الشبكة قد تكون بطيئة أو غير متاحة، قاعدة البيانات تحتاج إعداداً، والوقت الحقيقي يُصعّل اختبار الكود الزمني.
الحل: الاستبدال المؤقت (Mocking). في وقت الاختبار، تستبدل الجزء الخارجي بكائن تحكم فيه أنت — يتصرف كما تريد ويُخبرك كيف استُدعي.
ما هو Mock؟
Mock هو كائن يُحاكي سلوك كائن حقيقي. يمكنه:
- إرجاع قيمة مُحددة عند استدعائه
- رفع استثناء عند استدعائه
- تسجيل كيف استُدعي (عدد المرات، بأي معاملات)
المكتبة المعيارية توفّر unittest.mock — لا تثبيت مطلوب.
MagicMock — الكائن الذكي
MagicMock هو الأداة الرئيسية. يقبل أي استدعاء ويُعيد MagicMock آخر — يمكنك برمجة ما يُرجعه:
كيف يُساعد Mock في الاختبار
تخيّل دالة تحسب إجمالي الطلب وتُرسل إشعاراً بالبريد الإلكتروني. عند الاختبار، لا نريد إرسال بريد حقيقي — نريد فقط التحقق من أن الدالة طلبت إرسال البريد بالمعاملات الصحيحة.
patch — استبدال التبعيات المدمجة
أحياناً لا تستطيع تمرير التبعية كمعامل — الكود يستورد الوحدة مباشرة. patch يستبدل الاسم مؤقتاً في وقت الاختبار:
# الكود الأصلي — original code
import requests
def get_user_data(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
return response.json()
في الاختبار نستبدل requests.get بـ mock:
side_effect — سلوك ديناميكي
بدل return_value الثابت، side_effect يُشغّل دالة أو يرفع استثناء:
Fixtures في pytest — مراجعة مع مثال كامل
في pytest، الـ fixture يُنشئ مورداً ويُمرّره للاختبار تلقائياً. المثال التالي يوضح كيفية تنفيذه باستخدام unittest.setUp الذي يؤدي نفس الدور:
# أسلوب pytest — pytest style
@pytest.fixture
def user_service():
service = UserService(db=FakeDatabase())
yield service
service.cleanup()
def test_create_user(user_service):
user = user_service.create("أحمد", "[email protected]")
assert user.id is not None
المكافئ بـunittest:
# أسلوب unittest — unittest style
class TestUserService(unittest.TestCase):
def setUp(self):
self.service = UserService(db=FakeDatabase())
def tearDown(self):
self.service.cleanup()
def test_create_user(self):
user = self.service.create("أحمد", "[email protected]")
self.assertIsNotNone(user.id)
متى تستخدم Mock ومتى لا؟
استخدم Mock عندما:
- الكود يتصل بشبكة أو قاعدة بيانات خارجية
- التبعية بطيئة (API خارجي)
- تريد محاكاة حالات خطأ صعبة (انقطاع الشبكة)
- التبعية تُغيّر حالة خارجية (إرسال بريد، SMS)
- الكود يعتمد على الوقت الحالي (
datetime.now())
لا تستخدم Mock عندما:
- التبعية بسيطة ومتوفرة (دالة رياضية، معالجة نصوص)
- Mock يُصعّل الاختبار أكثر مما يُبسّطه
- تريد اختبار التكامل الفعلي بين المكونات
# لا داعي لـ Mock هنا — No need for mock
def test_add():
assert add(2, 3) == 5 # دالة بسيطة لا تبعيات
# Mock منطقي هنا — Mock makes sense
def test_send_order_notification():
mock_email = MagicMock()
# نختبر منطق الإشعار دون إرسال بريد حقيقي