---
title: "التجهيز العام"
type: "test-smell"
slug: "general-fixture"
url: "http://localhost:3000/ar/test-smells/general-fixture.md"
category: "روائح التجهيزات"
description: "تقوم عملية تهيئة مشتركة ببناء تجهيز واحد ضخم وشامل يغطي احتياجات كل الاختبارات، ولكن كل اختبار فردي يشغل جزءاً صغيراً جداً منه فقط."
---
# التجهيز العام

> تقوم عملية تهيئة مشتركة ببناء تجهيز واحد ضخم وشامل يغطي احتياجات كل الاختبارات، ولكن كل اختبار فردي يشغل جزءاً صغيراً جداً منه فقط.

## Signs and Symptoms

تقوم كتلة `beforeEach`/`setUp` واحدة (أو تهيئة مشتركة على مستوى الوحدة) بإنشاء الكثير من الكائنات، وتغذية العديد من السجلات، وربط عدة محاكيات — بينما يلمس أي اختبار معين واحداً أو اثنين منها فقط. المبدأ الموجه للاكتشاف من كتالوج روائح الاختبار هو ببساطة: _لا تُستخدم جميع الحقول التي تم إنشاؤها في التهيئة بواسطة جميع طرق الاختبار_.

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

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

```js
// رائحة: تجهيز واحد لكل شيء؛ ويستخدم كل اختبار جزءاً ضغيراً منه
let user, admin, product, cart, order, paymentGateway, shippingZone;

beforeEach(() => {
  user            = createUser({ name: 'Ada' });
  admin           = createAdmin();
  product         = createProduct({ price: 10 });
  cart            = createCart(user, [product]);
  order           = createOrder(cart);
  paymentGateway  = mockGateway();
  shippingZone    = createZone('EU');
});

test('cart subtotal sums its line items', () => {
  // يحتاج فقط إلى السلة + المنتج، ومع ذلك يدفع تكلفة المسؤول والطلب والبوابة والمنطقة...
  expect(cart.subtotal()).toBe(10);
});

```

## Reasons for the Problem

**لماذا يحدث ذلك**

* **المبالغة في تطبيق مبدأ DRY (عدم التكرار).** يتم دمج التهيئة في خطاف مشترك واحد لـ "تجنب التكرار"، وبذلك تُضاف احتياجات كل اختبار جديد إلى نفس التجهيز.
* **النمو العضوي.** يتراكم في التجهيز كائنات جديدة مع إضافة اختبارات جديدة، ولا يقوم أحد بتنظيف ما لم تعد الاختبارات الأقدم تستخدمه.
* **عطالة التهيئة الضمنية.** بمجرد وجود كتلة `beforeEach` كبيرة، تصبح إضافة سطر آخر أسهل من إنشاء تجهيز مركز للحالة الجديدة.

**لماذا يضر ذلك**

* **سهولة القراءة / الاختبارات كتوثيق.** يجب أن يُقرأ الاختبار كعلاقة واضحة بين السبب والنتيجة (cause→effect). عندما يكون معظم التجهيز عبارة عن ضوضاء غير ذات صلة، لا يستطيع القارئ تحديد ما الذي يؤدي فعلياً إلى النتيجة. ترياق ميسزاروس، وهو _التجهيز الأدنى (Minimal Fixture)_، موجود على وجه التحديد لأن الاختبار الذي يستخدم أصغر تجهيز ممكن يكون دائماً أسهل في الفهم.
* **سهولة الصيانة.** التغيير الذي يتطلبه اختبار واحد (مثل إعطاء `product` حقلاً مطلوباً جديداً) يفرض تعديل التهيئة المشتركة بين جميع الاختبارات، مما يخلق تأثيراً متسلسلاً من الأعطال ويقرن الاختبارات غير المترابطة ببعضها.
* **الموثوقية.** تتيح حالة التجهيز المشتركة والقابلة للتعديل للاختبارات التأثير على بعضها البعض وتجعل تحديد مكان الأعطال أمراً صعباً — وهو مصدر كلاسيكي لعدم الاستقرار المرتبط بالترتيب.
* **السرعة.** يدفع كل اختبار التكلفة الكاملة لبناء التجهيز بأكمله. يتم إدراج التجهيز العام ضمن أسباب بطء الاختبارات.
* **الثقة الزائفة.** تصبح الاختبارات محددة بشكل مفرط (over-specified) بناءً على تفاصيل تجهيز عارضة، لذا تنجح أو تفشل لأسباب لا علاقة لها بالسلوك قيد الاختبار.

## Treatment

احرص على استخدام **تجهيز أدنى (Minimal Fixture)**: يجهز كل اختبار ما يحتاجه فقط، ولا شيء أكثر.

1. **جرد الاستخدام.** لكل حقل مشترك، حدد الاختبارات التي تقرأه بالفعل. وأي حقل تستخدمه مجموعة فرعية فقط هو مرشح للنقل خارج التهيئة المشتركة.
2. **ادفع البناء إلى طرق الإنشاء / بناة بيانات الاختبار (التهيئة المفوضة Delegated Setup).** حافظ على إيجاز الاختبارات دون فرض تجهيز واحد عملاق — يستدعي كل اختبار بناءً (builder) ويتجاوز فقط الخصائص ذات الصلة به.
3. **فضل استخدام تجهيز جديد (Fresh Fixture) لكل اختبار** على استخدام تجهيز مشترك طويل الأمد، مما يلغي الاقتران بين الاختبارات والحالة المشتركة القابلة للتعديل.
4. **التقسيم حسب السيناريو.** إذا كانت مجموعات من الاختبارات تشترك فعلياً في تجهيز _صغير_، فافصلها إلى كتل `describe` مركزة (أو فئات اختبار)، لكل منها تهيئتها الصغرى الخاصة بها.
5. **احتفظ في `beforeEach` بما هو مشترك وصغير حقاً فقط;** وانقل الباقي إلى داخل الاختبار (inline) ليكون السبب والنتيجة متجاورين مع التحقق.

```js
// قبل: تجهيز عام في خطاف مشترك (انظر العلامات والأعراض)

// بعد: تهيئة صغرى ومفوضة — يبني كل اختبار فقط ما يحتاجه
test('cart subtotal sums its line items', () => {
  const cart = aCart().withItem(aProduct().price(10)).build();
  expect(cart.subtotal()).toBe(10);
});

test('checkout charges the payment gateway', () => {
  const gateway = mockGateway();
  const cart    = aCart().withItem(aProduct().price(10)).build();

  checkout(cart, gateway);

  expect(gateway.charge).toHaveBeenCalledWith(10);
});

```

يُقرأ كل اختبار الآن من الأعلى إلى الأسفل كقصة قائمة بذاتها، ويمتص البناة (builders) الكود المكرر، ولم يعد تغيير سيناريو واحد يزعج الآخرين.
