كائنات المحاكاة (Mock) والبيانات الثابتة (Fixtures) في C#: سر اختبار الوحدة المثالي

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

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

ابدأ الدورة

لماذا تكذب اختباراتك وكيف تصلح ذلك؟

\n\n

تخيل: تكتب كودًا مثاليًا بلغة C#، وتغطيه بالاختبارات، علامة خضراء تضيء، خط أنابيب CI سعيد. تطلق الإصدار... ثم ينهار الإنتاج. مألوف؟ على الأرجح، المشكلة ليست في الكود، بل في الاختبارات. لقد اختبرت نسخة "لعبة" من الكود الحقيقي، مرتبطة بقاعدة بيانات أو نظام ملفات أو API خارجي.

\n\n

مرحبًا بك في عالم كائنات المحاكاة (Mock) والبيانات الثابتة (Fixtures) — الركيزتان اللتان تقوم عليهما اختبارات الوحدة عالية الجودة في C#. بدونها، اختباراتك هي مجرد فحوصات تكامل هشة تنهار عند أدنى تغيير في البيئة. معها، تحصل على اختبارات سريعة ومعزولة وموثوقة تحمي الكود حقًا.

\n\n

في هذه المقالة، سنشرح: الفرق بين mock وstub وfake، وكيفية إعداد البيانات الثابتة بشكل صحيح، وسنقدم أمثلة حية مع المكتبات الشائعة: Moq وNSubstitute وAutoFixture. هيا بنا!

\n\n

Mock، Stub، Fake — ما الفرق؟ (تنبيه: هذا مهم)

\n\n

قبل كتابة الكود، دعنا نتفق على المصطلحات. العديد من المطورين يستخدمون كلمة "mock" لكل شيء، لكن هذا يؤدي إلى الارتباك. في تصنيف Gerard Meszaros (كتاب "xUnit Test Patterns") هناك تقسيم واضح:

\n\n
    \n
  • Dummy — كائن يتم تمريره لكنه لا يُستخدم أبدًا. مطلوب فقط لملء المعاملات.
  • \n
  • Stub — يقدم إجابات مُعدة مسبقًا على الاستدعاءات. تتحقق من الحالة (state verification).
  • \n
  • Fake — تنفيذ عملي لكن مبسط (مثل InMemoryRepository بدلاً من قاعدة البيانات الحقيقية).
  • \n
  • Mock — كائن يتحقق من السلوك (behavior verification). تؤكد أن طريقة معينة قد تم استدعاؤها بوسائط معينة.
  • \n
\n\n

عمليًا في C#، نستخدم غالبًا كائنات المحاكاة (Mock) (عبر Moq أو NSubstitute) للتحقق من التفاعلات، والبيانات الثابتة (Fixtures) (عبر AutoFixture) لتوليد بيانات الاختبار بسرعة. لكن دعنا نبدأ بالترتيب.

\n\n

كائنات المحاكاة في C#: عزل التبعيات

\n\n

تخيل أن لديك خدمة OrderService، التي عند تقديم طلب ترسل بريدًا إلكترونيًا وتحفظ البيانات في قاعدة البيانات. اختبارها "بشكل حقيقي" يعني إرسال رسائل بريد إلكتروني والكتابة في قاعدة البيانات في كل مرة. هذا بطيء وغير موثوق ومكلف.

\n\n

الحل — عزل التبعيات. نستبدل الكائنات الحقيقية (EmailSender، DbContext) بكائنات محاكاة ونتحقق من أن الخدمة تستدعي الطرق الصحيحة بالمعاملات الصحيحة.

\n\n

مثال مع Moq (المكتبة الأكثر شيوعًا)

\n\n
public interface IEmailSender\n{\n    void Send(string to, string subject, string body);\n}\n\npublic interface IOrderRepository\n{\n    void Save(Order order);\n}\n\npublic class OrderService\n{\n    private readonly IEmailSender _emailSender;\n    private readonly IOrderRepository _repository;\n\n    public OrderService(IEmailSender emailSender, IOrderRepository repository)\n    {\n        _emailSender = emailSender;\n        _repository = repository;\n    }\n\n    public void PlaceOrder(Order order)\n    {\n        // منطق الأعمال\n        _repository.Save(order);\n        _emailSender.Send(order.CustomerEmail, "تم تأكيد الطلب", $"طلبك رقم #{order.Id} تم تقديمه.");\n    }\n}
\n\n

الآن لنكتب اختبارًا باستخدام Moq:

\n\n
[TestFixture]\npublic class OrderServiceTests\n{\n    [Test]\n    public void PlaceOrder_ShouldSendEmail_WhenOrderIsValid()\n    {\n        // Arrange\n        var mockEmailSender = new Mock<IEmailSender>();\n        var mockRepository = new Mock<IOrderRepository>();\n        var service = new OrderService(mockEmailSender.Object, mockRepository.Object);\n        var order = new Order { Id =                

المدونات

توصيات الكتب