اختبار في Go: الوحدة، التكامل، E2E على المستوى المتقدم

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

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

ابدأ الدورة

مقدمة: لماذا الاختبار المتقدم هو أمر لا بد منه في Go



تشتهر Go (Golang) ببساطتها وأدواتها المدمجة للاختبار. ومع ذلك، عندما ينمو المشروع خارج نطاق "Hello, World"، لم يعد go test القياسي كافياً. على المستوى المتقدم، يواجه المطورون أسئلة: كيف نعزل الاختبارات بشكل صحيح، وكيف نختبر العمل مع قاعدة البيانات، وكيف نحاكي الخدمات الخارجية، وكيف ننظم فحصاً شاملاً للتطبيق بأكمله.



في هذه المقالة، سنستعرض ثلاثة مستويات من الاختبار — Unit و Integration و E2E — مع أمثلة من الممارسة الواقعية. ستتعلم كيفية استخدام الموكات والحاويات لاختبارات التكامل وكيفية بناء خط أنابيب موثوق لـ CI/CD. المادة موجهة للمطورين الذين هم على دراية بأساسيات Go ويريدون رفع جودة كودهم إلى مستوى جديد.



1. اختبارات الوحدة: العزل، الموكات ونمط الاختبارات المعتمدة على الجداول



تتحقق اختبارات الوحدة من عمل وحدة واحدة (دالة، طريقة) في عزلة تامة عن التبعيات الخارجية. في Go، غالباً ما تُستخدم الواجهات ومكتبات إنشاء الموكات مثل testify/mock أو gomock.



1.1. الواجهات كأساس للاختبار



لاستبدال خدمة حقيقية بموك، يجب أن يعتمد الكود على واجهة، وليس على تنفيذ محدد. لنأخذ مثالاً على خدمة إرسال الإشعارات.



// notifier.gotype Notifier interface {    Send(to string, message string) error}

type UserService struct { Notifier Notifier}

func (s *UserService) NotifyUser(userID string) error { // منطق الحصول على البريد الإلكتروني للمستخدم email := userID + "@example.com" return s.Notifier.Send(email, "Welcome!")}


1.2. إنشاء موك باستخدام testify/mock



تثبيت المكتبة: go get github.com/stretchr/testify.



// notifier_mock.go (في الحزمة user_test)type NotifierMock struct {    mock.Mock}

func (m *NotifierMock) Send(to string, message string) error { args := m.Called(to, message) return args.Error(0)}


1.3. الاختبارات المعتمدة على الجداول



هذا هو النمط القياسي في Go لاختبار حالات متعددة. يحسن قابلية القراءة ويبسط إضافة اختبارات جديدة.



// user_service_test.gofunc TestNotifyUser(t *testing.T) {    tests := []struct {        name      string        userID    string        mockError error        expectErr bool    }{        {"success", "user1", nil, false},        {"fail_send", "user2", errors.New("send error"), true},    }

for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockNotifier := new(NotifierMock) service := &UserService{Notifier: mockNotifier}

mockNotifier.On("Send", tt.userID+"@example.com", "Welcome!").Return(tt.mockError)

err := service.NotifyUser(tt.userID) if tt.expectErr { assert.Error(t, err) } else { assert.NoError(t, err) } mockNotifier.AssertExpectations(t) }) }}


أفضل الممارسات: استخدم t.Run للاختبارات الفرعية — يتيح ذلك تشغيل كل حالة على حدة ورؤية أي منها فشل تحديداً.



2. اختبارات التكامل: العمل مع قواعد البيانات والخدمات الخارجية



تتحقق اختبارات التكامل من تفاعل عدة مكونات: كود Go مع قاعدة البيانات، خادم API مع ذاكرة التخزين المؤقت، إلخ. التحدي الرئيسي هو إدارة حالة البيئة. هنا تأتي Testcontainers للإنقاذ.



2.1. Testcontainers لـ Go



تسمح Testcontainers بإنشاء حاويات Docker مباشرة من الاختبارات. وهذا يوفر بيئة نظيفة ومعزولة لكل تشغيل.



التثبيت: go get github.com/testcontainers/testcontainers-go



2.2. مثال: اختبار

المدونات

توصيات الكتب