اختبار الوحدة: شبكة الأمان المعروفة أيضًا باسم كيفية النوم ليلاً
إن تغطية الكود بنسبة 100% هي مقياس تافه. كيفية كتابة اختبارات وحدة ذات معنى باستخدام Jest والتي تكتشف الأخطاء الحقيقية دون منع إعادة البناء.
لماذا تتحدث Maison Code عن هذا
في Maison Code Paris، نعمل كضمير معمari لعملائنا. غالبًا ما نرث حزمًا “حديثة” تم بناؤها دون فهم أساسي للحجم.
نناقش هذا الموضوع لأنه يمثل نقطة تحول حاسمة في النضج الهندسي. التنفيذ الصحيح يميز MVP الهش عن منصة مؤسسية مرنة يمكنها التعامل مع حركة مرور الجمعة السوداء.
أزمة الثقة
تحتاج إلى إعادة هيكلة الوظيفة الأساسية.
ربما تكون وظيفة calculateTax() في الخروج.
إنه فوضوي. لقد تم دمج عبارات if. ويستخدم بناء الجملة القديم. تريد تنظيفه.
لكنك مرعوب.
تسأل نفسك: “إذا لمست هذا وخرقت قاعدة ضريبية محددة لعملاء الجملة الألمان، فهل سأعرف ذلك؟”
إذا كانت الإجابة “لا”، فأنت مشلول.
لذلك تتركها. يبقى “الرمز القديم”. إنه يتعفن. يصبح “المجلد المخيف” الذي لا يلمسه أحد.
اختبارات الوحدة هي العلاج لهذا الشلل.
يقومون بتجميد سلوك الكود في الوقت المناسب.
وهي تضمن ما يلي: “يجب أن تُرجع هذه الوظيفة، في ضوء الإدخال 2، 4.”
إذا استمر هذا العقد، فيمكنك إعادة كتابة التنفيذ الداخلي كيفما تريد.
تسهل الاختبارات ** إعادة الهيكلة العدوانية **. بدون اختبارات، إعادة البناء هي مجرد “تخمين”.
لماذا تناقش Maison Code اختبار الوحدة
في Maison Code، غالبًا ما نقوم بتنفيذ “مهام الإنقاذ” على الأنظمة الأساسية القديمة. نجد قواعد التعليمات البرمجية التي يخشى المطورون نشرها. “لا تلمس وحدة UserSync، فسوف تنكسر إذا نظرت إليها بشكل خاطئ.” هذا الخوف يشل العمل. تستغرق الميزات الجديدة أشهرًا لأنه يتم إنفاق 80% من الوقت على اختبار الانحدار اليدوي. نحن نؤمن بأن الكود القابل للاختبار هو كود نظيف. نحن ننفذ مجموعات قوية لاختبار الوحدات باستخدام Jest/Vitest لإعادة ثقة الفريق. عندما تكون الاختبارات باللون الأخضر، يمكنك النشر. بهذه البساطة. نحول “النشر يوم الجمعة” من كابوس إلى لا حدث.
الأداة: الدعابة / فيتيست
- الدعابة: شاغل الوظيفة. تتم صيانته بواسطة ميتا. قياسي في إنشاء تطبيق React / Next.js. قوية ولكنها ثقيلة.
- فيتست: المتحدي. مدعوم من فيت. إنه مطابق وظيفيًا لـ Jest (واجهة برمجة التطبيقات المتوافقة) ولكنه أسرع بـ 10 مرات لأنه يستخدم وحدات ES أصلاً. سوف نستخدم صيغة Jest هنا، لأنها تنطبق على كليهما. المفاهيم عالمية.
الإستراتيجية: ما الذي يجب اختباره؟
هذا هو المكان الذي تفشل فيه الفرق. فخ “التغطية بنسبة 100%”. يتوق المديرون إلى “تغطية الكود بنسبة 100%”. لقد وضعوها في OKRs. وهذا يؤدي إلى اختبارات عديمة الفائدة.
** اختبار سيئ (اختبار إطار العمل) **:
زر ثابت = ({ label }) => <button>{label}</button>;
اختبار ("عرض الزر"، () => {
تقديم(<Button label="Go" />);
توقع(screen.getByText('Go')).toBeInTheDocument();
});
يحتوي هذا الاختبار على قيمة منخفضة. إنه يختبر React. نحن نعلم أن React تعمل.
لديها ** صيانة عالية **. إذا قمنا بإعادة تسمية label إلى text، فسيتم إيقاف الاختبار، على الرغم من أن التطبيق يعمل. وهذا “سلبي كاذب”.
استراتيجية الاختبار الجيدة: ركّز على منطق الأعمال والحالات المتطورة.
1. الوظائف النقية (أعلى عائد على الاستثمار)
تعتمد الوظيفة النقية فقط على الوسائط وتقوم بإرجاع قيمة. لا توجد آثار جانبية. لا توجد مكالمات API. هذه متعة للاختبار. يتم تشغيلها بالمللي ثانية.
// المنطق.ts
/**
* يحسب الخصم على أساس طبقة العملاء.
* القواعد:
* - يحصل VIP على خصم 20%.
* - السعر السلبي يلقي خطأ.
* - يحصل الموظف على خصم 50% .
*/
وظيفة التصدير احسب الخصم (السعر: الرقم، النوع: 'VIP' | 'العادي' | 'الموظف'): الرقم {
إذا (السعر <0) ألقى خطأ جديد ("السعر السلبي مستحيل")؛
إذا (اكتب === 'الموظف') سعر الإرجاع * 0.5؛
إذا (اكتب === 'VIP') سعر الإرجاع * 0.8؛
سعر العودة
}
// المنطق.test.ts
وصف('حساب الخصم', () => {
test('يمنح خصمًا بنسبة 20% لكبار الشخصيات', () => {
// ترتيب
السعر الثابت = 100؛
نوع ثابت = 'VIP'؛
// قانون
نتيجة const = countDiscount(price, type);
// تأكيد
توقع(نتيجة).toBe(80);
});
test('يمنح الموظف خصمًا بنسبة 50%', () => {
توقع(calculateDiscount(100, 'Employee')).toBe(50);
});
test('يمنح خصمًا بنسبة 0% على المنتجات العادية', () => {
توقع(calculateDiscount(100, 'Regular')).toBe(100); // 100 * 1 = 100
});
اختبار ("يرمي السعر السلبي"، () => {
// ملاحظة: نقوم بتغليف رمز الاتصال في دالة حتى يتمكن Jest من اكتشاف الخطأ
توقع(() => احسب الخصم(-10, 'منتظم')).toThrow('السعر السلبي');
});
});
هذا قوي. إنه يوثق قواعد العمل بشكل أفضل من التعليقات.
2. الاستهزاء (الشر الذي لا بد منه)
تتفاعل معظم التعليمات البرمجية مع العالم (قاعدة البيانات، API، LocalStorage). لا يمكنك تشغيل استدعاء API حقيقي في اختبار الوحدة. إنه بطيء، وغير مستقر، ويتطلب بيانات اعتماد. أنت ** تسخر ** من التبعية.
// ملف تعريف المستخدم.ts
استيراد { fetchUserFromStripe } من './api'؛
تصدير وظيفة غير متزامنة getUserStatus(id: string) {
مستخدم const = ينتظر fetchUserFromStripe(id); // التبعية الخارجية
إذا قام (user.delinquent) بإرجاع "محظور"؛
إرجاع "نشط" ؛
}
// userProfile.test.ts
استيراد { fetchUserFromStripe } من './api'؛
jest.mock('./api'); // محاكاة تلقائية للوحدة. يستبدل الوظائف الحقيقية بـ jest.fn()
اختبار ("إرجاع محظور إذا كان المستخدم جانحًا"، غير متزامن () => {
// إعداد الاستجابة الوهمية
(fetchUserFromStripe as jest.Mock).mockResolvedValue({ id: '1', delinquent: true });
حالة const = انتظار getUserStatus('1');
توقع(الحالة).toBe('محظور');
// التحقق من استدعاء واجهة برمجة التطبيقات بشكل صحيح لضمان تمرير المعرف
توقع(fetchUserFromStripe).toHaveBeenCalledWith('1');
});
تحذير من المستهزئين: المستهزئون يكذبون.
إذا غيّر fetchUserFromStripe الحقيقي تنسيق الإرجاع الخاص به (على سبيل المثال، أصبح delinquent isDelinquent)، فسيستمر اختبارك في النجاح (لأنك سخرت من التنسيق القديم)، لكن تطبيقك سيتعطل.
استخدم TypeScript للحفاظ على مزامنة النماذج مع الأنواع الحقيقية.
اختبار اللقطة: ميزة أم خطأ؟
قدم Jest “لقطات”.
توقع (مكون).toMatchSnapshot ().
يقوم بإجراء تسلسل للمكون المقدم إلى ملف نصي (__snapshots__/comp.test.js.snap).
إذا قمت بتغيير H1 إلى H2، يفشل الاختبار.
المشكلة: يتعامل المطورون مع هذا الأمر على أنه مصدر إزعاج.
يجرون الاختبار. لقد فشل. يكتبون jest -u (تحديث) دون النظر.
اختبارات اللقطة هشة.
أفضل الممارسات: استخدم اللقطات للمكونات الصغيرة جدًا والثابتة (الأيقونات ورموز التصميم) حيث يكون أي تغيير مريبًا. لا تستخدمها للصفحات المعقدة.
اختبار التعليمات البرمجية غير المتزامنة (مصيدة عدم المزامنة/الانتظار)
اختبار الوعود أمر صعب. خطأ شائع: نسيان “الانتظار” أو “المعلمات”.
// سيئ: ينتهي الاختبار قبل حل الوعد
اختبار ("اختبار غير متزامن سيئ"، () => {
fetchData().then(data => {
توقع(بيانات).toBe('زبدة الفول السوداني');
});
});
إذا فشل fetchData، فقد يستمر الاختبار في اجتيازه (إيجابي كاذب) أو انتهاء المهلة.
** جيد **:
اختبار ("اختبار متزامن جيد"، غير متزامن () => {
بيانات ثابتة = انتظار fetchData();
توقع(بيانات).toBe('زبدة الفول السوداني');
});
بالنسبة للمؤقتات (على سبيل المثال، setTimeout)، استخدم المؤقتات الزائفة:
jest.useFakeTimers().
jest.advanceTimersByTime(1000).
يتيح لك هذا اختبار تأخير لمدة 10 ثوانٍ في 1 مللي ثانية.
مناقشة TDD (التطوير القائم على الاختبار).
“اكتب الاختبار أولاً.” الإيجابيات: يفرض عليك تصميم واجهة برمجة التطبيقات (API) قبل التنفيذ. النتائج في رمز نظيف للغاية ومنفصل. السلبيات: سرعة أولية أبطأ. يكون الأمر صعبًا عندما “تستكشف” (النماذج الأولية) ولا تعرف الهيكل النهائي. الحكم: استخدم TDD للمنطق الخوارزمي المعقد (على سبيل المثال، تحليل ملف CSV، وحساب الضرائب، ومعالجة السلسلة). لا تستخدمه لمكونات واجهة المستخدم البسيطة حيث تقوم بالتكرار على وحدات البكسل.
التكامل مقابل الوحدة: شكل الكأس
قام كينت سي دودز بإضفاء الطابع الرسمي على “كأس الاختبار”.
- التحليل الثابت (ESLint، TypeScript): يرصد الأخطاء المطبعية. (أسرع/رخيص).
- اختبارات الوحدة: اختبار الوظائف النقية. (سريع).
- اختبارات التكامل: اختبر الاتصال بين المكونات. (البقعة الحلوة).
- ** اختبارات E2E **: اختبر المتصفح الكامل. (بطيء/مكلف).
التوصية: اكتب في الغالب اختبارات التكامل. اختبار “نموذج تسجيل الدخول” + “زر الإرسال” + “ApiMock”. لا تختبر “SubmitButton” بشكل منفصل. اختبر أن “النقر على إرسال يستدعي واجهة برمجة تطبيقات تسجيل الدخول”.
الأسئلة الشائعة
س: كيف يمكنني اختبار الوظائف الخاصة؟ ج: لا. اختبار واجهة برمجة التطبيقات العامة. الوظيفة الخاصة هي تفاصيل التنفيذ. إذا قمت باختبار وظائف خاصة، فإنك تقيد نفسك بهذا التنفيذ. تفقد القدرة على إعادة البناء.
س: تكامل CI/CD؟ ج: يجب إجراء الاختبارات عند كل طلب سحب. إذا فشل اختبار npm، فيجب تعطيل زر الدمج. وهذا يحافظ على سياسة “السيد الأخضر”.
الخلاصة
الاختبارات هي استثمار. أنت تدفع مقدما (الوقت). تحصل على أرباح إلى الأبد (الاستقرار، وسرعة إعادة البناء، والتوثيق). تعتبر قاعدة التعليمات البرمجية التي لا تحتوي على اختبارات “قاعدة تعليمات برمجية قديمة” منذ اليوم الأول. قاعدة التعليمات البرمجية مع الاختبارات هي “الأصل”. ويقدر في القيمة.
هل التنفيذ مشلول؟
إذا كان فريقك يعاني من الخوف من خرق القواعد القديمة، فإن Maison Code هو الحل. نقوم بتنفيذ “غارات إعادة البناء” حيث نقوم بمراجعة قاعدة التعليمات البرمجية الخاصة بك، وإضافة تغطية اختبارية، وتنظيف الديون التقنية التي تبطئك. نحن ندرب فريقك على كيفية كتابة اختبارات غير سيئة.