كائنات المحاكاة (Mock) والبيانات الثابتة (Fixtures) في اختبارات TypeScript: من المبتدئ إلى المحترف

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

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

ابدأ الدورة

مقدمة: لماذا الاختبارات بدون كائنات محاكاة مثل القهوة بدون كافيين؟

\n\n

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

\n\n

هنا يأتي دور كائنات المحاكاة (mock) والبيانات الثابتة (fixtures). فهي تسمح بعزل الكود عن العالم الخارجي وتجعل الاختبارات سريعة وموثوقة وقابلة للتنبؤ. في هذه المقالة باللغة العربية، سنشرح بأمثلة واقعية كيفية استخدام mock في TypeScript بشكل صحيح، وما هي أنواع البيانات الثابتة لـ TypeScript، وكيف لا نرتكب الأخطاء.

\n\n

المقالة مكتوبة لـ PythonLib - البوابة التعليمية حيث نعلم كتابة كود عالي الجودة. هيا بنا!

\n\n

ما هي كائنات المحاكاة والبيانات الثابتة؟

\n\n

كائنات المحاكاة: ممثلون بديلون في الكود الخاص بك

\n\n

كائن المحاكاة (أو mock ببساطة) هو بديل لكائن حقيقي يحاكي سلوكه. أنت تقول: "عند استدعاء طريقة sendEmail()، أعد true". ولا يتدخل الاختبار في البريد الإلكتروني الحقيقي.

\n\n

في TypeScript، يتم إنشاء الكائنات المحاكاة غالبًا باستخدام مكتبات Jest أو Vitest. وهي تسمح بالتحقق من استدعاء الطريقة، ومعرفة الوسائط التي تم تمريرها، وعدد مرات الاستدعاء.

\n\n

البيانات الثابتة: بيانات معدة مسبقًا للاختبارات

\n\n

البيانات الثابتة (fixtures) هي مجموعات بيانات معدة مسبقًا. على سبيل المثال، كائن مستخدم، قائمة طلبات، أو تكوين تطبيق. بدلاً من إنشائها يدويًا في كل مرة، تقوم بتخزين البيانات الثابتة في ملفات منفصلة.

\n\n

الجمع بين mock + fixture هو معيار اختبار TypeScript في عام 2024.

\n\n

تطبيق عملي: كتابة اختبارات مع كائنات محاكاة وبيانات ثابتة

\n\n

إعداد البيئة

\n\n

لأمثلتنا، سنستخدم Jest (على الرغم من أن كل شيء يعمل أيضًا مع Vitest). تأكد من تثبيت التبعيات:

\n\n
npm install --save-dev jest @types/jest ts-jest
\n\n

أضف إلى jest.config.js:

\n\n
module.exports = {\n  preset: 'ts-jest',\n  testEnvironment: 'node',\n};
\n\n

مثال 1: محاكاة خدمة إرسال البريد الإلكتروني

\n\n

لنفترض أن لدينا خدمة إشعارات:

\n\n
// notificationService.ts\nexport class EmailService {\n  async sendEmail(to: string, subject: string, body: string): Promise<boolean> {\n    // الإرسال الحقيقي - لن نلمسه\n    return true;\n  }\n}\n\nexport class NotificationManager {\n  constructor(private emailService: EmailService) {}\n\n  async notifyUser(email: string, message: string): Promise<string> {\n    const result = await this.emailService.sendEmail(email, 'إشعار', message);\n    return result ? 'تم الإرسال' : 'خطأ';\n  }\n}
\n\n

الآن لنكتب اختبارًا باستخدام كائن محاكاة:

\n\n
// notificationService.test.ts\nimport { NotificationManager, EmailService } from './notificationService';\n\ndescribe('NotificationManager', () => {\n  it('يجب إرسال الإشعار وإرجاع "تم الإرسال"', async () => {\n    // إنشاء كائن محاكاة\n    const mockEmailService: jest.Mocked<EmailService> = {\n      sendEmail: jest.fn().mockResolvedValue(true),\n    };\n\n    const manager = new NotificationManager(mockEmailService);\n    const result = await manager.notifyUser('test@test.com', 'مرحبًا!');\n\n    expect(result).toBe('تم الإرسال');\n    expect(mockEmailService.sendEmail).toHaveBeenCalledWith(\n      'test@test.com',\n      'إشعار',\n      'مرحبًا!'\n    );\n  });\n\n  it('يجب إرجاع "خطأ" إذا فشل الإرسال', async () => {\n    const mockEmailService: jest.Mocked<EmailService> = {\n      sendEmail: jest.fn().mockResolvedValue(false),\n    };\n\n    const manager = new NotificationManager(mockEmailService);                

المدونات

توصيات الكتب