ConstructiCat Logo
CodeBust.
Browse section ▾

التأكيد الزائد.

التأكيد الزائد هو تأكيد يقارن قيمة بنفسها أو بقيمة حرفية مساوية لها بالبناء، فتكون نتيجته محددة مسبقاً ولا يمكنه أن يفشل أو يكتشف انحداراً.

##Signs and Symptoms

تأكيد زائد هو التأكيد الذي تُحدَّد نتيجته قبل أن يعمل الكود قيد الاختبار — المعاملان المتوقع والفعلي نفس القيمة، أو كلاهما قيم حرفية معروفة بالتساوي (أو عدم التساوي). يُعرّفه فهرس xUnit/Test Smells (Peruma وآخرون) بأنه «طريقة اختبار تحتوي على عبارة تأكيد تكون فيها المعاملات المتوقعة والفعلية متطابقة»، ويُلاحظ أن التأكيد لذلك «إما صحيح دائماً أو خاطئ دائماً.»

كيف تتعرف عليه:

  • assertEquals/toBe/toEqual يتشاركان نفس التعبير أو المتغير كحجتين.
  • قيمة حرفية منطقية مؤكدة على نفسها، مثل assertTrue(true) أو expect(true).toBe(true).
  • مقارنة يستطيع المصرِّف/المُدقق إثبات ثباتها، كـexpect(x === x).toBe(true).
  • «فحص سلامة» يُعيد صياغة ثابت أعلنته للتو بدلاً من تجربة النظام.
// رائحة: النتيجة ثابتة، الكود قيد الاختبار لا يشارك أبداً
test('user is active', () => {
  expect(true).toBe(true);            // يجتاز دائماً
  const status = 'active';
  expect(status).toBe('active');      // يعيد صياغة القيمة الحرفية، لا يُثبت شيئاً
  expect(user.id).toEqual(user.id);   // قيمة مقارنة بنفسها
});

علامة دالة موثوقة: يمكنك حذف كود الإنتاج كلياً والتأكيد يجتاز أخضراً.

##Reasons for the Problem

لماذا يحدث

  • تصحيح متبقٍّ. يُلاحظ الفهرس صراحةً أن هذه الرائحة «يُدخلها المطورون لأغراض التصحيح ثم ينسونها» — عنصر نائب مُرمَّز مثل assertTrue(true) يبقى حتى عملية الحفظ.
  • انجراف النسخ-اللصق / إعادة الهيكلة. تُستبدل متغير في كلا طرفي assertEquals، أو تُعاد تسمية قيمة قيد الاختبار فيتطابق الطرف المتوقع والطرف الفعلي على الرمز نفسه.
  • تطابق بالبناء. تأكيد قيمة مقابل القيمة الحرفية التي عُيِّنت منها للتو، بدلاً من التأكيد مقابل توقع مُشتق باستقلالية.
  • مسرحية التغطية. يُضاف تأكيد لمجرد تلبية قواعد «كل اختبار يجب أن يؤكد»، دون التحقق من أي شيء ذي معنى.

لماذا يضر

  • ثقة زائفة. الاختبار أخضر دائماً ويُحسب ضمن حجم المجموعة والتغطية، ولكنه لا يتحقق من شيء. لا يستطيع اكتشاف انحدار، فيُخفي ثغرات في شبكة الأمان.
  • الموثوقية بلا معنى. اختبار لا يمكنه أن يفشل لا يُعطي أي إشارة؛ واختبار خاطئ دائماً عبء ميت يُتجاهل أو يُتخطى بـskip.
  • قابلية القراءة. يُضيع القراء جهداً في إعادة بناء السلوك المقصود من تأكيد يؤكد حقيقة بديهية؛ لم يعد الاختبار يوثّق متطلباً.
  • قابلية الصيانة. تتراكم التأكيدات الزائدة كضجيج، وتُضخّم المقاييس، وتتآكل الثقة في المجموعة، مما يشجع الناس على التوقف عن قراءة التأكيدات بعناية.

##Treatment

استبدل الحقيقة البديهية بفحص يربط قيمة متوقعة معروفة باستقلالية بـالنتيجة الفعلية التي ينتجها النظام قيد الاختبار.

الخطوات:

  1. حدد التأكيد ذا النتيجة الثابتة — نفس المعامل على كلا الجانبين، أو قيمتان حرفيتان متساويتان.
  2. حدد النية الحقيقية. ما السلوك الذي كان هذا الاختبار يُقصد التحقق منه؟ إن لم يكن ثمة شيء، فالتأكيد (أو الاختبار بأكمله) ميت ويجب حذفه.
  3. أكّد على مخرجات SUT، لا على المدخلات. أدخل للنظام مدخلات حقيقية وقارن نتيجته المحسوبة بقيمة متوقعة مُرمَّزة محسوبة يدوياً — لا بإحدى مدخلاته/متغيراته الخاصة.
  4. أبقِ المتوقع/الفعلي متمايزَين. تأكد أن معامل التوقع ثابت كتبته بقصد وأن المعامل الفعلي هو القيمة المُعادة من الكود قيد الاختبار (يُصلح أيضاً رائحة ترتيب الحجج الخاطئة المرتبطة).
  5. أعِد التشغيل مع كسر التنفيذ (غيّره) للتأكد من أن التأكيد يمكنه أن يفشل فعلاً.
// قبل — زائد: النتيجة ثابتة
test('discount', () => {
  const total = 100;
  expect(total).toBe(100);          // يعيد صياغة القيمة الحرفية
  expect(applyDiscount).toBe(applyDiscount); // قيمة مقارنة بنفسها
});

// بعد — ذو معنى: مدخل معروف -> نتيجة متوقعة مستقلة
test('applies a 10% discount', () => {
  expect(applyDiscount(100, 0.1)).toBe(90); // نتيجة SUT مقارنةً بقيمة متوقعة محسوبة يدوياً
});

إن تُرك عنصر نائب مثل assertTrue(true) من التصحيح، احذفه؛ وإن كان بديلاً لفحص حقيقي، اكتب ذلك الفحص.

##Detected by

  • sonar javascript:S5863يجب ألا تُعطى التأكيدات الحجة ذاتها مرتين
  • sonar java:S5863يجب ألا تُعطى التأكيدات الحجة ذاتها مرتين
  • eslint no-constant-binary-expressionعدم السماح بتعبيرات لا تؤثر العملية فيها على القيمة (يكتشف المقارنات الذاتية / التأكيدات الدائمة الصحة مثل assert(a === a))