العزل بدون ألم: Mock و Fixtures في اختبارات Go

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

عندما يعيق الواقع: لماذا نحتاج إلى mocks و fixtures في Go؟

\n\n

تخيل أنك تختبر دالة ترسل إشعارًا عبر البريد الإلكتروني. كل تشغيل للاختبار هو رسالة حقيقية إلى صندوق البريد. بعد عشرة اختبارات، سيكون صندوقك ممتلئًا، وسيلعن زملاؤك البريد العشوائي. أو الأسوأ: الدالة تتصل بـ API خارجي مدفوع أو يعمل مرة واحدة يوميًا.

\n\n

في عالم Go (أو Golang، كما يُطلق عليه غالبًا) الاختبار ليس مجرد علامة في قائمة المهام. إنه أساس الموثوقية. ولكن لجعل الاختبارات سريعة ومستقلة، نحتاج إلى عزل الكود عن العالم الخارجي. وهنا يأتي دور كائنات mock (mocks) و fixtures.

\n\n

في هذه المقالة، سنشرح كيفية استبدال التبعيات (mocking) وإعداد البيانات (fixtures) بشكل صحيح في Go. ستتعلم كيفية كتابة اختبارات لا تفشل بسبب الشبكة أو قاعدة البيانات أو الوقت من اليوم. هيا بنا!

\n\n

ما هو Mock و Fixture في سياق Go؟

\n\n

كائنات Mock: بدائل للأنظمة المعقدة

\n\n

كائنات Mock هي "بدائل" برمجية تحاكي سلوك المكونات الحقيقية. بدلاً من استدعاء قاعدة بيانات حقيقية أو خدمة HTTP، نستخدم كائنًا يعيد استجابات محددة مسبقًا.

\n\n

في Go، تعتبر mocks مهمة بشكل خاص لأن اللغة تشجع العمل مع الواجهات (interfaces). إذا كان الكود الخاص بك يعتمد على واجهة، يمكنك بسهولة استبدال التنفيذ الحقيقي بتنفيذ اختباري.

\n\n

Fixtures: تجهيز المسرح

\n\n

Fixtures هي البيانات أو الحالة التي يجب إعدادها قبل بدء الاختبار. على سبيل المثال، إنشاء قاعدة بيانات مؤقتة، ملؤها بسجلات اختبارية، أو إعداد نظام الملفات.

\n\n

على عكس mocks التي تستبدل السلوك، تقوم fixtures بتجهيز البيئة. إنها تجيب على السؤال: "ما الذي يجب أن يكون جاهزًا حتى يبدأ الاختبار؟"

\n\n

كتابة mocks في Go: من البسيط إلى المعقد

\n\n

أبسط طريقة: تنفيذ الواجهة يدويًا

\n\n

لا تتطلب Go مكتبات خاصة لإنشاء mocks. إذا كان لديك واجهة، يمكنك كتابة تنفيذ اختباري لها يدويًا. هذا مثالي للمشاريع الصغيرة.

\n\n

لنفترض أن لدينا واجهة لإرسال الإشعارات:

\n\n
package notifier\n\ntype Notifier interface {\n    Send(to string, message string) error\n}\n\n// التنفيذ الحقيقي (ليس للاختبارات)\ntype EmailNotifier struct{}\n\nfunc (e *EmailNotifier) Send(to string, message string) error {\n    // إرسال بريد إلكتروني حقيقي\n    return nil\n}
\n\n

في ملف الاختبار، ننشئ mock:

\n\n
package notifier\n\nimport "testing"\n\ntype MockNotifier struct {\n    Called bool\n    LastTo string\n    LastMsg string\n}\n\nfunc (m *MockNotifier) Send(to string, msg string) error {\n    m.Called = true\n    m.LastTo = to\n    m.LastMsg = msg\n    return nil\n}\n\nfunc TestSendAlert(t *testing.T) {\n    mock := &MockNotifier{}\n    // نستخدم mock في الدالة التي تقبل Notifier\n    SendAlert(mock, "user@test.com", "Hello!")\n    \n    if !mock.Called {\n        t.Error("لم يتم استدعاء طريقة Send")\n    }\n    if mock.LastTo != "user@test.com" {\n        t.Errorf("مستلم غير صحيح: %s", mock.LastTo)\n    }\n}
\n\n

الإيجابيات: تحكم كامل، لا تبعيات.
\nالسلبيات: الكثير من الكود النمطي للواجهات المعقدة.

\n\n

استخدام testify/mock: قوي ومريح

\n\n

مكتبة testify هي المعيار الفعلي لـ mocks في Go. توفر إطارًا مرنًا لإنشاء mocks مع التحقق من الوسائط وعدد الاستدعاءات.

\n\n

التثبيت:

\n
go get github.com/stretchr/testify
\n\n

مثال باستخدام testify/mock:

\n\n
package user\n\nimport (\n    "testing"\n    "github.com/stretchr/testify/mock"\n)\n\n// تعريف الواجهة\ntype UserRepository interface {\n    GetUser(id int) (string, error)\n}\n\n// إنشاء هيكل mock\ntype                

المدونات

توصيات الكتب