---
title: "اختبار تفاصيل التنفيذ"
type: "test-smell"
slug: "testing-implementation-details"
url: "http://localhost:3000/ar/test-smells/testing-implementation-details.md"
category: "روائح المحاكاة"
description: "يقوم الاختبار بالتحقق من كيفية عمل الكود داخلياً — مثل الحقول الخاصة، أو استدعاءات الدوال الداخلية، أو بنية DOM، أو فئات CSS — بدلاً من التحقق من السلوك الملاحظ الذي يعتمد عليه المستخدم الفعلي."
---
# اختبار تفاصيل التنفيذ

> يقوم الاختبار بالتحقق من كيفية عمل الكود داخلياً — مثل الحقول الخاصة، أو استدعاءات الدوال الداخلية، أو بنية DOM، أو فئات CSS — بدلاً من التحقق من السلوك الملاحظ الذي يعتمد عليه المستخدم الفعلي.

## Signs and Symptoms

تتعرف على هذه المشكلة عندما يتجاوز الاختبار العقد العام (public contract) ويقوم بتثبيت الآلية الداخلية التي تقف خلفه. العلامات الشائعة:

* التحقق من **الحالة الخاصة/الداخلية (private/internal state)** أو الحقول، أو استدعاء الدوال الخاصة مباشرة (غالباً عبر التحويلات القسرية للأنواع (casts)، أو الانعكاس (reflection)، أو استخدام `// @ts-expect-error`).
* التجسس (Spying) أو التحقق من **استدعاء دالة مساعدة داخلية** (مثل `expect(internalCalc).toHaveBeenCalled()`) بدلاً من التحقق من النتيجة.
* اختبارات واجهة المستخدم التي تستعلم باستخدام **فئة الـ CSS، أو الوسم (tag)، أو خطافات `data-*`، أو موضع DOM** بدلاً من الاستعلام باستخدام الدور (role) أو التسمية (label) أو النص — على سبيل المثال: `container.querySelector('.btn-primary > span:nth-child(2)')`، أو `wrapper.state()`، أو `wrapper.find('SomeChildComponent').props()`.
* اختبارات اللقطات (Snapshot tests) لشجرة التصيير بالكامل أو الكائنات الداخلية المتسلسلة، بحيث يفشل الاختبار عند إجراء أي تعديل طفيف في المارك-أب.
* الاختبارات التي **تتعطل مع كل عملية إعادة هيكلة** على الرغم من أن الميزة لا تزال تعمل بشكل صحيح (السلبيات الزائفة)، وبالعكس تظل ناجحة بعد وجود خلل منطقي لأنها تتحقق فقط من التوصيلات (الإيجابيات الزائفة).

```js
// مشكلة (SMELL): يربط الاختبار بالتفاصيل الداخلية للمكون وبنية الـ DOM
test('counter increments', () => {
  const wrapper = mount(<Counter />);
  wrapper.instance().handleClick();          // يستدعي دالة خاصة مباشرة
  expect(wrapper.state('count')).toBe(1);    // يتحقق من الحالة الداخلية
  expect(wrapper.find('.count-display').text()).toBe('1'); // محدد CSS هِش
});

```

يظهر نفس الشكل في جانب الخادم (server-side): مثل `expect(service._cache.size).toBe(1)` أو التحقق من التسلسل الدقيق للاستدعاءات الداخلية التي تقوم بها الدالة.

## Reasons for the Problem

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

* الوصول إلى الحالة الداخلية _سهل للغاية_ — فالحقول العامة، أو الدوال المساعدة المصدرة، أو `container.querySelector` تكون متاحة مباشرة، في حين أن تشغيل السلوك الفعلي يتطلب إعداداً أكثر تعقيداً.
* السعي وراء مقاييس التغطية (coverage metrics): فيبدو اختبار كل دالة خاصة بنسبة 1:1 أمراً شاملاً.
* يدفع الاستخدام المكثف للمحاكاة الافتراضية (mocking) المطورين للتحقق مما إذا كان "قد تم استدعاء هذا المساعد" بدلاً من التحقق مما إذا كان "قد حدث الشيء الصحيح".
* الأدوات التي تشجع على ذلك: مثل واجهات برمجة التطبيقات للتصيير السطحي (shallow rendering) أو `instance()` أو `state()`، أو جلب عقد DOM باستخدام فئة الـ CSS (class name).

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

* **قابلية الصيانة / الهشاشة.** هذا ما يسميه "ميسزاروس" بالـ _الاختبار الهش (Fragile Test)_ الناتج عن _البرمجيات المحددة بشكل مفرط (Overspecified Software)_: حيث يقوم الاختبار بتثبيت سلوك لم يطلبه المستخدم أبداً، وبالتالي فإن عمليات إعادة الهيكلة غير الضارة (إعادة تسمية دالة، أو إعادة هيكلة المارك-أب، أو تغيير حقل خاص) تؤدي إلى تعطل الاختبارات الناجحة دون سبب حقيقي. وتصبح الاختبارات عبئاً على عملية إعادة الهيكلة بدلاً من أن تكون شبكة أمان لها.
* **الثقة الزائفة (الخطر الأساسي، وفقاً لـ Kent C. Dodds).** تفشل اختبارات تفاصيل التنفيذ في _كلا_ الاتجاهين الخاطئين: **السلبيات الزائفة (false negatives)** (يفشل الاختبار على الرغم من أن الميزة لا تزال تعمل بشكل صحيح) و**الإيجابيات الزائفة (false positives)** (ينجح الاختبار على الرغم من تعطل الميزة — لأنك تحققت من التوصيلات وليس من النتيجة). وفي كلتا الحالتين، تتوقف مجموعة الاختبارات عن إخبارك بالحقيقة.
* **المقروئية.** يوثق الاختبار _كيفية_ بناء الكود، وليس _ما يضمنه_. لا يمكن للقارئ معرفة أي السلوكيات تهم بالفعل، ولا يعود الاختبار صالحاً ليكون مثالاً للاستخدام أو مواصفة للكود (spec).
* **الاقتران (Coupling).** يثبت الاختبار قرارات التصميم الحالية، مما يثبط عمليات إعادة الهيكلة التي كان من المفترض أن تجعلها الاختبارات آمنة.

## Treatment

قم بالاختبار من خلال **العقد العام (public contract)** — وهو نفس السطح الذي يلمسه المتصل الفعلي أو المستخدم — وتحقق من **المخرجات الملاحظة**: قيم الإرجاع، أو الأخطاء التي تم إلقاؤها، أو الأحداث المنبعثة، أو الحالة المحفوظة، أو واجهة المستخدم المصيّرة/المرئية.

1. **حدد المستهلك (consumer).** بالنسبة للوحدة البرمجية (module)، فإن المستهلك هو واجهة برمجة التطبيقات المصدرة (exported API) الخاصة بها؛ وبالنسبة لمكون واجهة المستخدم، فإنه المستخدم (النقرات، الكتابة) وما يمكنه رؤيته.
2. **قم بتقديم المدخلات بنفس الطريقة التي يقوم بها المستهلك**، وليس عن طريق استدعاء دوال خاصة. قم بتوليد نقرة حقيقية بدلاً من استدعاء معالج الحدث (handler)؛ واستدعِ الدالة العامة بدلاً من الدالة المساعدة الداخلية.
3. **تحقق من النتائج وليس من التفاصيل الداخلية.** استبدل فحوصات `state()` أو الحقول الخاصة أو `toHaveBeenCalled` بالتحقق مما ينتج عن العملية.
4. **استعلم عن عناصر واجهة المستخدم من خلال إمكانية الوصول (accessibility) وليس البنية** — مثل الدور (role)، والتسمية (label)، والنص — بدلاً من فئات CSS، أو الوسوم، أو `nth-child`.
5. **توقف عن اختبار الدوال الخاصة مباشرة.** قم بتغطيتها من خلال اختبار الدالة العامة التي تستخدمها؛ وإذا كانت الوحدة الخاصة معقدة لدرجة تتطلب اختبارات منفردة، فهذا مؤشر على الحاجة إلى استخلاصها في وحدة برمجية منفصلة تمتلك واجهة برمجة تطبيقات عامة خاصة بها.
6. **احصر استخدام التحقق من المحاكاة/التجسس (mock/spy) على الحدود الحقيقية للبرنامج** (الشبكة، الوقت، بوابة الدفع) حيث يكون _الاستدعاء نفسه_ هو السلوك الملاحظ — وليس للمساعدين الداخليين.

```js
// قبل: يختبر تفاصيل التنفيذ
const wrapper = mount(<Counter />);
wrapper.instance().handleClick();
expect(wrapper.state('count')).toBe(1);
expect(wrapper.find('.count-display').text()).toBe('1');

// بعد: يختبر السلوك الملاحظ عبر العقد العام الموجه للمستخدم
render(<Counter />);
await userEvent.click(screen.getByRole('button', { name: /increment/i }));
expect(screen.getByText('1')).toBeInTheDocument();

```

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

## Detected by

- **eslint-testing-library** `testing-library/no-node-access` — no-node-access (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-node-access.md)
- **eslint-testing-library** `testing-library/no-container` — no-container (https://github.com/testing-library/eslint-plugin-testing-library/blob/main/docs/rules/no-container.md)
