الكود الموروث

التعامل بفعالية مع الكود الموروث 5 – Test Covering

هذا هو الجزء الخامس من سلسلة (التعامل بفاعلية مع الكود الموروث) والتي نقوم فيها بتلخيص الكتاب الذي يحمل نفس الاسم. يمكنك الاطلاع على كامل مقالات السلسلة من هنا.

مازلنا نتحدث في فضاء تغيير الكود والدلالات التي يجب أن ننتبه إليها كمبرمجين عند القيام بهذه التغييرات. و اتفقنا مسبقا أن أحد أهم الأدوات المساعدة في تغيير الكود هي الـ Tests إذا أنها تعطينا إجابةً feedback فيما إذا كنا نقوم بتغيير الكود بالطريقة الصحيحة أم لا. و اتفقنا أيضا أنه من الحكمة عند البدء بتغيير الكود أن نقوم بتغطيته بمجموعة من الـ Tests والتي تعمل كشبكة أمان.

في هذه المقالة سأقدم من كتاب Working effectively with legacy code  مثال عملي على ما سبق و نخطوا خطوة بعيدا عن الكلام النظري لنرى كيف نستطيع تغطية الكود بالـ tests.

لنأخذ المثال التالي: مجموعة من الـ classes يبدو أنها جزء من نظام يعمل على تخزين الفواتير في قاعدة بيانات ويقوم بتحديثها.
في هذا المثال, نريد إجراء تعديل على كل من الـ methods التالية  :

  • ()getResponseText ضمن InvoiceUpdateResponder
  • ()getValue  ضمن Invoice class

ولكي نتمكن من إجراء التعديل باحترافيه، يجب أن نحيط هذه ال classes ب tests لضمان أن الكود مازال يعمل كما هو متوقع بعد إجراء التعديل.

ولكتابة test لـ class معين نحن بحاجة لإنشاء instance من هذا الJ class.

  • في حالة ال Invoice class يبدو الأمر غير معقد إذ إن الـ constructor لا يتطلب أي parameters.
  • ولكن في حالة ال InvoiceUpdateResponder نرى أن الـ constructor يتطلب DBConnection اي اتصال بقاعدة البيانات، ويتطلب أيضا InvoiceUpdateServlet مما يصعب علينا مهمة كتابة ال test. لماذا ؟

إذا تتبعنا محتوى المقالة السابقة التي تتحدث عن ال unit test نلاحظ أنه لا يمكن لـ test أن يتخاطب مع قاعدة بيانات حقيقية. هل من المعقول ان ننشئ قاعدة بيانات مخصصة لل test؟ سيكلفنا هذا الكثير من العمل و نحن الآن لسنا بصدد الاهتمام بقاعدة البيانات، كل ما نريد عمله هو كتابة tests حول getResponseText و getValue.

الـ constructor بحاجة أيضا لـ InvoiceUpdateServlet, و بالترجمة يكون هذا ال Invoice UpdateServlet عبارة عن بريمج صغير لتحديث الفاتورة. وهذا أيضا خارج اهتمامنا.

إذاً طريقة تصميم ال InvoiceUpdateResponder class لا تساعد المبرمج على كتابة tests، إذ أنه مصمم بحيث أنه يعتمد بشكل مباشر و صريح على classes أُخرى.

وللأسف يبدو أننا بحاجة لكسر هذه الاعتماديات dependencies لنتمكن من كتابة الـ tests. مثل أن نعدل على الكود بحيث نمرر المعلومات التي يحتاج أن يعرفها الـ InvoiceUpdateResponder عن هذا الـ InvoiceUpdateServlet عوضا عن تمريره كـ object كامل أو غيرها من الحلول.

وهنا نقع في دائرة مفرغة, لتغيير الكود نحن بحاجة كتابة  tests و لكتابة ال tests نحن بحاجة لتغيير الكود. هذه هي معضلة الكود الموروث.

إعتماد الـ classes على بعضها هو واحد من أكثر المشاكل الحرجة في تطوير البرمجيات. وغالبا عند التعامل مع الكود الموروث يتطلب الكثير من العمل على كسر هذه العلاقات بين ال classes حتى تصبح التغييرات أسهل.

إذا.. ما الحل الآن؟ كيف نقوم بالخروج من هذه الدائرة؟ كيف نقوم بكتابة الـ tests بدون تغيير الكود؟

في مثالنا الحالي يمكننا كما قلنا سابقا، أن نقوم بتمرير المعلومات التي يحتاج أن يعرفها الـ InvoiceUpdateResponder عن الـ InvoiceUpdateServlet عوضا عن تمريره كـ object كامل. مثلا ربما يحتاج الـ InvoiceUpdateResponder فقط أن يعرف قائمة ال ID الخاصة بالفواتير التي نريد أن نجري عليها عمليات ما ولاشيء غير ذلك من المعلومات التي يمررها الـ Responder.

ومن الممكن أيضا أن نستعيض عن تمرير الـ DBConnection بـ interface ونقوم باستخدام هذه الـ interface في الـ tests عوضا عن استخدام الـ DBConnection، كما في الشكل التالي:

هذه التقنية في مفهوم كسر الاعتمادية dependency breaking تسمى بالـ primitive parameter و Extract interface. سنخوض لاحقا بتفاصيل أكثر عن هذه التقنيات.

في هذه المرحلة إذاً، قمنا بتغيير الكود لنتمكن من كتابة tests لنقوم بالتغيير المرجو منذ البداية. ولكن مالذي يؤكد أن هذه التغيرات لم تؤدِّ إلى تغير وظيفي في الكود؟
حسنا, لا يمكننا أن نتأكد من خلو البنية الجديدة من الاخطاء، ولابد من تغيير الكود بهذه الطريقة لنتمكن من كتابة ال tests. ولكن إذا نظرنا إلى التغيرات نجد أننا كنا حريصين على إضافة أقل التعديلات الممكنة. وهذا هو التصرف الصحيح في مثل هذا الحالة التي لا يوجد لدينا feedback حول إذا ما كانت التغيرات الجديدة تقدم أخطاء جديدة.

لننظر للموضوع من وجهة نظر أخرى: مالفائدة من هذه الـ interface المضافة هنا؟ لاشيء إلا أنها تسهل كتابة الـ tests. لربما يرى بعض المبرمجين أن هذه التغيرات الجديدة على بنية الكود غريبة أحياناً .نعم، عملية الـ dependency breaking هي خطوة حرجة يتصاحب معها تغيرات في بنية الـ classes والـ code structure ولكن للضرورة أحكام.

الحال هنا تماما مثل اجراء عملية جراحية والندوب التي تتركها العملية غير جميلة و غير محببة ولكنها حتما ساعدتنا على الشفاء من المرض.

الخلاصة

عند كل عملية تعديل على كود، سواء نحن من قام بكتابته أو كان كودًا قديمًا، نحن بحاجة لإحاطة منطقة التغيير بقدر من الـ tests تضمن أن تعطينا feedback ودلالات. ولكن لكتابة هذه الـ tests نفسها نحن أحيانا بحاجة للتعديل على الكود. يوجد تقنيات عديدة للقيام بهذا التغيير الاخير بهدف كتابة الـ tests ويرافقه حذر و صرامة من قبل المبرمج للقيام بأقل التغييرات الممكنة.

حنين عبد الله

مطورة تطبيقات اندرويد, مهتمة بإثراء المحتوى العربي بكل ما يخص البرمجة.   
عنواني على تويتر @haneen0abdallah

التعليقات: 1 ضع تعليقك

0791653277 يقول:

سلام عليكم
اخي الكريم انا لدي هاتف اس 6 ايدج بلاس و الاندرويد 5.1.1 ولا ينزل الأندرويد الأخير و لى هو النوجا

Leave a Reply

Your email address will not be published. Required fields are marked *