الاختبار الطفري في TypeScript: كسر الكود لتحسينه

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

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

ابدأ الدورة

هل أنت متأكد من أن اختباراتك تعمل حقًا؟

\n\n

تخيل: لديك تغطية كود بنسبة 95%، جميع الاختبارات خضراء، CI/CD يمر بدون أخطاء. الفريق سعيد، والمدير هادئ. ولكن في يوم من الأيام يظهر خطأ في الإنتاج. تفتح الكود، تنظر إلى الاختبارات وتفهم: إنها فقط تتحقق من أن الدالة تُرجع رقمًا، لكنها لا تنظر إلى قيمته. أو الأسوأ من ذلك - الاختبار مكتوب بحيث ينجح في أي حال.

\n\n

موقف مألوف؟ مرحبًا بك في عالم حيث نسبة التغطية هي مجرد رقم، وليست ضمانًا للجودة. هنا يظهر على المسرح الاختبار الطفري (mutation testing) - منهجية تجعل اختباراتك تعاني حقًا وتثبت جدارتها.

\n\n

في هذه المقالة، سنشرح كيفية دمج الاختبار الطفري في مشروع TypeScript باستخدام Stryker، ولماذا قد يصبح أداتك المفضلة الجديدة (أو مصدرًا جديدًا للصداع).

\n\n

ما هو الاختبار الطفري بكلمات بسيطة؟

\n\n

الاختبار الطفري هو عندما تأخذ الكود الخاص بك، تقوم عمدًا بإدخال أخطاء صغيرة فيه (طفرات) وترى ما إذا كانت اختباراتك ستفشل. إذا لم تفشل الاختبارات - فهذا يعني أنك تختبر بشكل غير صحيح. إذا فشلت - فأنت ممتاز، ولكن تم القضاء على المتحول.

\n\n

يبدو هذا ساديًا؟ ربما. لكنه يعمل. تخيل أنك مهندس ضمان جودة (QA) تسللت سرًا إلى الكود واستبدلت === بـ ==، أو > بـ <، أو أزلت استدعاء دالة. يجب على الاختبار الجيد أن يلاحظ ذلك. أما الاختبار السيئ فسيتجاهله.

\n\n

في TypeScript، مع كتابته الصارمة للأنواع، يكون الاختبار الطفري مفيدًا بشكل خاص: فهو يكتشف الحالات التي تكون فيها الأنواع صحيحة، لكن المنطق ليس كذلك.

\n\n

لماذا تغطية الكود العادية (code coverage) هي وهم؟

\n\n

لنفترض أن لديك دالة:

\n\n
function calculateDiscount(price: number, isPremium: boolean): number {\n  if (isPremium) {\n    return price * 0.8;\n  }\n  return price;\n}
\n\n

واختبار:

\n\n
test('returns number', () => {\n  const result = calculateDiscount(100, true);\n  expect(typeof result).toBe('number');\n});
\n\n

التغطية؟ 100%. الاختبار؟ ناجح. ولكن ماذا لو استبدلنا price * 0.8 بـ price * 0.5؟ سيظل الاختبار ناجحًا - لأنه يتحقق فقط من النوع، وليس القيمة. هذا هو الشعور الزائف بالأمان.

\n\n

الاختبار الطفري سيسلط الضوء على مثل هذه الطفرة ويقول: "مرحبًا، اختبارك لم يلاحظ أنني غيرت الخصم!". وسيتعين عليك كتابة تحقق صحيح.

\n\n

كيف يعمل الاختبار الطفري في TypeScript: خطوة بخطوة

\n\n

يمكن تقسيم العملية بأكملها إلى ثلاث مراحل:

\n\n
    \n
  1. توليد المتحولين - تقوم الأداة بنسخ الكود الخاص بك وإجراء تغيير واحد في كل مرة. على سبيل المثال، تغيير && إلى || أو إزالة استدعاء دالة.
  2. \n
  3. تشغيل الاختبارات - لكل متحول، يتم تشغيل اختباراتك.
  4. \n
  5. تحليل النتائج - إذا فشلت الاختبارات، يتم "قتل" المتحول. إذا لم تفشل، فإنه "ينجو"، وهذه منطقة خطر.
  6. \n
\n\n

المقياس الرئيسي هو mutation score (نسبة المتحولين المقتولين). 100% هو المثالي، ولكن في الممارسة العملية، 80-90% يعتبر ممتازًا بالفعل.

\n\n

الأداة رقم 1 لـ TypeScript: Stryker

\n\n

في عالم TypeScript و JavaScript، ملك الاختبار الطفري هو Stryker (StrykerJS). إنه سريع، ويعمل مع Jest و Vitest و Mocha وحتى مع اختبارات المتصفح. يمكن إعداده في 5 دقائق.

\n\n

لنقم بالتثبيت:

\n\n
npm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner
\n\n

لننشئ ملف الإعدادات stryker.conf.js:

\n\n
// stryker.conf.js\nexport default {\n  mutate: ['src/**/*.ts', '!src/**/*.spec.ts'], // ما الذي سيتم تحويره\n  testRunner: 'jest',\n  reporters: ['html', 'clear-text', 'progress'],\n  jest: {\n    projectType: 'custom',\n    configFile: 'jest.config.ts',\n  },\n  concurrency: 4, // عدد المتحولين المتزامنين                

المدونات

توصيات الكتب