كيفية محاكاة (Mock) دوال void في Mockito: دليل للمبتدئين مع أمثلة

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

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

ابدأ الدورة

مقدمة: لماذا تعتبر دوال void كابوسًا للمبتدئين؟

\n\n

هل سبق لك أن كتبت اختبار وحدة (unit test) في Java باستخدام Mockito وفجأة اصطدمت بجدار؟ يبدو الأمر بسيطًا: تقوم بمحاكاة كائن، وتضبط سلوك الدوال التي تُرجع شيئًا (سلسلة نصية، رقم، قائمة). ولكن عندما تصل إلى دوال void - تلك التي لا تُرجع شيئًا، بل تقوم فقط بفعل شيء داخليًا (مثل حفظ البيانات، إرسال بريد إلكتروني، تسجيل إجراء) - يبدأ التوقف.

\n\n

لماذا؟ لأن الطريقة المألوفة when(mock.someMethod()).thenReturn(something) لا تعمل هنا. المترجم ببساطة لن يسمح لك بكتابة when(mock.voidMethod()).thenReturn(...) - دالة void لا تُرجع شيئًا، و thenReturn لا معنى لها هنا.

\n\n

هذا السؤال تحديدًا - "How to mock void methods with Mockito" - حصد آلاف المشاهدات على Stack Overflow. واليوم سنقوم بحله مرة واحدة وإلى الأبد. ستتعلم ليس فقط كيفية محاكاة دوال void، بل أيضًا كيفية القيام بذلك بشكل صحيح وجميل وبدون ألم.

\n\n

هيا بنا! 🚀

\n\n

ما هي دالة void من منظور الاختبار؟

\n\n

دالة void هي دالة تقوم بإجراء ولكنها لا تُرجع نتيجة. على سبيل المثال:

\n\n
public class EmailService {\n    public void sendWelcomeEmail(String email) {\n        // هنا الإرسال الفعلي للبريد الإلكتروني\n        System.out.println("نرسل بريدًا إلكترونيًا إلى " + email);\n    }\n}
\n\n

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

\n\n

لهذا، يوفر Mockito صياغة خاصة باستخدام doNothing() و doThrow() و doAnswer() و doCallRealMethod().

\n\n

1. الحالة الأكثر شيوعًا: doNothing() - ببساطة إخفاء دالة void

\n\n

إذا كنت بحاجة إلى ألا تفعل دالة void شيئًا عند استدعائها - استخدم doNothing(). هذا هو السلوك الافتراضي لـ Mockito لدوال void، ولكن في بعض الأحيان من المفيد تحديده صراحة (مثلًا من أجل readability أو عند استخدام spy).

\n\n
import org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\n\nimport static org.mockito.Mockito.*;\n\n@ExtendWith(MockitoExtension.class)\npublic class EmailServiceTest {\n\n    @Mock\n    private EmailService emailService;\n\n    @Test\n    void testDoNothing() {\n        // نضبط: عند استدعاء sendWelcomeEmail لا تفعل شيئًا\n        doNothing().when(emailService).sendWelcomeEmail(anyString());\n\n        // نستدعي الدالة\n        emailService.sendWelcomeEmail("test@example.com");\n\n        // نتحقق من أن الدالة قد تم استدعاؤها بالفعل\n        verify(emailService).sendWelcomeEmail("test@example.com");\n    }\n}
\n\n

مهم: إذا كنت تستخدم @Mock، فلن تحتاج إلى كتابة doNothing() - Mockito يجعل دوال void "فارغة" افتراضيًا. ولكن إذا كنت تستخدم @Spy (جاسوس)، فسيتم استدعاء الدالة الحقيقية، وستحتاج إلى doNothing() لمنع ذلك.

\n\n

2. doThrow() - محاكاة خطأ في دالة void

\n\n

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

\n\n
@Test\nvoid testDoThrow() {\n    // نضبط: عند استدعاء sendWelcomeEmail قم بإلقاء RuntimeException\n    doThrow(new RuntimeException("خطأ في إرسال البريد الإلكتروني"))\n            .when(emailService).sendWelcomeEmail(anyString());\n\n    // الآن عند استدعاء الدالة سيكون هناك استثناء\n    assertThrows(RuntimeException.class, () -> {\n        emailService.sendWelcomeEmail("fail@example.com");\n    });\n\n                

المدونات

توصيات الكتب