ردي على أولئك الذين يعتقدون أن قيمة TDD مبالغ فيها

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

لم أركز على هذا ، وصلت المحادثة إلى Agile. كان العميل ، بشكل عام ، منفتحًا على فكرة اختبار منهجيات جديدة وتحسين عمليات عملها. ولكن - فقط حتى ذكرت التطور من خلال الاختبار (TDD ، التطوير القائم على الاختبار). الجواب الوحيد على ذلك كان العبارة التالية: "قيمة TDD مبالغ فيها".



لم يكن الأمر مؤلمًا بالنسبة لي أن أسمعها فحسب ، بل جعلني أدرك أن TDD هي واحدة من تلك المنهجيات الرشيقة التي قد تبدو مثل شيء مثل "الأساطير الحضرية". هذا ما جعلني أكتب هذه المادة ، التي أود أن أناشد فيها أولئك الذين يشكون في قيمة TDD.

ما هو TDD؟


اسمحوا لي أن أبدأ بتحديد TDD. هذه هي استراتيجية تطوير البرمجيات ، والتي أثناء تنفيذها ، عند إنشاء برنامج ، يتم توجيههم بالكامل بكتابة الاختبارات. في الواقع ، هذا أمر مفهوم من اسم هذه المنهجية. اقترح كينت بيك هذه الفكرة في كتابه "التنمية من خلال الاختبار" ، الذي كتب عام 2003. يتضمن استخدام TDD الخطوات الثلاث التالية:

  1. كتابة اختبار يفشل بوظيفة صغيرة.
  2. تنفيذ الوظائف التي تؤدي إلى النجاح في اجتياز الاختبار.
  3. إعادة هيكلة الكود القديم والجديد من أجل الحفاظ عليه في حالة جيدة التنظيم وقابلة للقراءة.

تُعرف هذه العملية أيضًا باسم دورة "الأحمر والأخضر وإعادة البيع".

هنا مثال بسيط لاستخدام TDD. هنا نريد كتابة طريقة في Python تم تصميمها لحساب متغير Cباستخدام نظرية فيثاغورس.

إليك رمز الاختبار (الملف test.py):

#!/usr/bin/env python

"""
test.py
"""

import unittest

from main import *

class TestDrivenDevelopment(unittest.TestCase):

        def test_pythagorean_theorem(self):
                a, b = 3, 4
                self.assertEqual(calculate_side_c(a, b), 5)

if __name__ == '__main__':
    unittest.main()

إليك رمز الطريقة ( main.py) ، الذي لا يحدث فيه شيء حتى الآن:

#!/usr/bin/env python

"""
main.py
"""

def calculate_side_c(side_a, side_b):
    return True

حاول الآن تشغيل الاختبار.


فشل الاختبار (الجزء "الأحمر" من دورة TDD)

أعد كتابة الكود ، مع إحضار المحتوياتmain.pyإلى النموذج التالي:

#!/usr/bin/env python

"""
main.py
"""

def calculate_side_c(side_a, side_b):
    c_squared = (side_a * side_a) + (side_b * side_b)
    c = c_squared ** 0.5
    return c 

شغّل الاختبار مرة أخرى.


سار الاختبار بشكل جيد (الجزء "الأخضر" من دورة TDD)

والآن نقومmain.pyبإعادة بناءالكود:

#!/usr/bin/env python

"""
main.py
"""

def calculate_side_c(side_a, side_b):
    return ((side_a ** 2) + (side_b ** 2)) ** 0.5


كان الاختبار ناجحًا للشفرة الخاضعة لإعادة البناء (الجزء "إعادة البيع" من دورة TDD)

مرت 17 عامًا منذ نشر كتاب التطوير من خلال الاختبار. ولكن حتى اليوم ، في عام 2020 ، لا توجد حتى الآن إجابة نهائية على السؤال المهم جدًا للعديد من الناس حول ما إذا كانت فوائد TDD تستحق الوقت والجهد لتنفيذ هذه المنهجية. ونتيجة لذلك ، لا يحاول العديد من المطورين حتى التطوير من خلال الاختبار.

لدعم TDD ، سألت المطورين لماذا لم يستخدموا هذه المنهجية. فيما يلي 4 إجابات سمعتها في أغلب الأحيان:

  1. لدينا قسم ضمان الجودة. اختبارات الكتابة هي وظيفتهم.
  2. يمكن أن يستغرق إنشاء الاختبارات التي قد تكون هناك حاجة فيها إلى moki و stubs الكثير من الوقت والطاقة.
  3. TDD ليس له مزايا على التنمية التقليدية.
  4. TDD بطيء.

دعونا نحلل هذه الأفكار.

لدينا قسم ضمان الجودة. اختبارات الكتابة هي وظيفتهم


هذه الإجابة على سؤالي حول لماذا لا يستخدم شخص ما TDD سيجعلني أضحك قليلاً. أنا مؤيد كبير لمبدأ " أنت تبنيها ، تديرها " ، عندما يكون الشخص الذي كتب الشفرة هو المسؤول عنها. لذلك ، يزعجني عندما أرى أن المطورين يتصرفون كما لو كانت مهمتهم الوحيدة هي كتابة التعليمات البرمجية الوظيفية.

أنا مقتنع تمامًا بأن المطورين مسؤولون عن التأكد من أن الشفرة لديهم الصفات التالية:

  • صحة - تلبية احتياجات العمل.
  • الوضوح.
  • قابلية الاختبار.
  • القابلية للتوسعة.
  • بساطة.

أعتقد أن نقل مهمة كتابة الاختبارات إلى قسم ضمان الجودة لا ينتمي إلى قائمة المسؤوليات الوظيفية للمطور. لدى المتخصصين في قسم ضمان الجودة أمور أكثر أهمية. يجب عليهم عدم قضاء وقت عملهم ، بشكل أساسي ، في اختبار الرمز باستخدام طريقة "الصندوق الأسود".

يمكن أن يستغرق إنشاء الاختبارات التي قد تكون هناك حاجة فيها إلى moki و stubs الكثير من الوقت والطاقة


أفهم مشاعر أولئك الذين يقولون ذلك. على سبيل المثال ، تحتاج إلى تنفيذ طريقة تأخذ ثلاث قيم إدخال مختلفة. كل منها عبارة عن كائن ، وليس لكل من هذه الكائنات سمات اختيارية. يجب اختبار كل هذا من خلال التحقق من الخيارات المختلفة للطريقة. للقيام بذلك ، تحتاج إلى تكوين بذرة وموكاس ، وكذلك كتابة كمية كبيرة من التعليمات البرمجية الإضافية. وكل هذا لاختبار طريقة واحدة فقط. لقد كنت بالفعل في حالات مماثلة. لكني أرى كل هذا من منظور آخر.

عندما أفكر في تقليل الجهد والوقت المستغرق في كتابة الاختبارات ، أتوصل إلى مبادئ برمجة SOLID وهرم الاختبار.

ابدأ مع SOLID من هنا.. هنا ، أود أن أسهب في الحديث عن حقيقة أن SOLID في الاختصار يتم تمثيله بالحرف S ، أي على مبدأ المسؤولية الوحيدة (مبدأ المسؤولية الفردية). هذه فكرة وفقًا للطرق والطبقات التي يجب أن تحل مشكلة واحدة فقط. يجب أن يعملوا بحيث لا تؤثر أنشطتهم على بقية النظام.

أفهم أن معظم الأساليب في برامج المؤسسة تؤدي مهامًا أكثر صعوبة من العثور على مجموع عدة أرقام يتم تمريرها إلى هذه الأساليب. ولكن لا يمكن للمرء أن ينكر حقيقة أن استخدام الأساليب التي تهدف إلى حل مشكلة واحدة في مشروع يسهل الاختبار إلى حد كبير.

لنتحدث عن هرم الاختبار.


هرم الاختبار (الصورة مأخوذة من هنا )

غالبًا ما يستخدم هرم الاختبار للعثور على النسبة الصحيحة للأنواع المختلفة من الاختبارات في المشروع.

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

يتطلب إجراء اختبارات الوحدة لاختبار جزء صغير من النظام أيضًا مستوى أقل بكثير من التكامل مع بقية النظام. هذا يجعل كتابة مثل هذه الاختبارات أسهل. تم تصميم منهجية TDD حصريًا لاستخدام اختبارات الوحدة.

بالطبع ، كل شيء أسهل من القيام به ، وكتابة معظم الاختبارات ، حتى اختبارات الوحدات ، تحتاج إلى بذل بعض الجهد. ومع ذلك ، إذا كان يبدو أن كتابة اختبار الوحدة تستغرق الكثير من الجهد ، فيمكنك اختبار نفسك عن طريق طرح أسئلة على نفسك:

  • هل هذا اختبار معياري ، أم ينبغي اعتباره اختبارًا لمستوى أعلى ، كاختبار خدمة؟
  • هل الكود مصمم جيدًا ، فهل كل فئة وطريقة تتوافق مع مبدأ المسؤولية الوحيدة (هل الكود مقترن بإحكام شديد)؟

إذا اتضح أن اختبارات الكتابة تستغرق الكثير من الوقت والجهد ، فغالبًا ما يشير هذا إلى أن الرمز يمكن وربما يحتاج إلى إعادة البناء.

TDD ليس له مزايا على التنمية التقليدية


ومن المفارقات ، حقيقة أن TDD ليس لها مزايا على التنمية التقليدية يقال بشكل رئيسي من قبل المبرمجين الذين لم يحاولوا حتى العمل بأسلوب TDD. ولكن حتى تجرب شيئًا ما ، لا يمكنك أن تفهم ما إذا كان هذا جيدًا أم سيئًا. حتى فرقة Coldplay تعرف أنه "إذا لم تحاول أبدًا ، فلن تعرف أبدًا".

TDD قوتان رئيسيتان.

الميزة الأولى لـ TDD واضحة جدًا. إذا كان شخص ما ، قبل كتابة رمز العمل ، يكتب دائمًا اختبارات لهذا الرمز ، فعندئذٍ يكون للمبرمج دائمًا ، بحكم تعريفه ، رمز اختبار ذاتي تحت تصرفه. إليك ما يكتبه مارتن فاولر حول هذا الأمر: "لديك رمز اختبار ذاتي إذا كان بإمكانك تشغيل سلسلة من الاختبارات التلقائية على أساس التعليمات البرمجية ، وعند الانتهاء بنجاح من الاختبارات ، يمكنك التأكد من أن الشفرة خالية من العيوب الهامة".

بمعنى آخر ، يعني استخدام TDD أن الكود يعمل تمامًا كما يحتاج المبرمج.

هذه حقيقة رائعة ، لأنها تعطي ثقة جدية في استقرار الرمز لأي تغيير سلبي. يسمح استخدام TDD للمبرمج بمعرفة ما إذا كان هناك شيء في قاعدة الكود قد تدهور نتيجة لإعادة الهيكلة أو تمديد الكود. والأفضل من ذلك أنه يتيح لك معرفة ما إذا كان هناك أي أخطاء في النظام.

يسمح رمز الاختبار الذاتي لفرق التطوير بالاستفادة حقًا من مزايا التكامل المستمر وأنظمة نشر التعليمات البرمجية.

الميزة الرئيسية الثانية لـ TDD هي أن منهجية التطوير هذه تجعل المبرمجين ، حتى قبل كتابة كود العمل ، يفكرون في ما سيكتبونه وكيف سيكتبونه.

التفكير في الشفرة قبل كتابتها يؤدي إلى حقيقة أن المطور غارق حقًا في جوهر احتياجات العمل ويأخذ في الاعتبار الحالات الحدودية والصعوبات المحتملة في تنفيذ الآليات المناسبة. ونتيجة لذلك ، عند كتابة كود العمل ، يتم بذل الجهد على ما تحتاج إليه بالضبط. بالإضافة إلى ذلك ، يؤدي استخدام TDD إلى حقيقة أن المطورين يخططون لأنظمة أجهزة مستقبلية من حيث هيكلها وبنيتها. إذا تم التخطيط لهذه الأشياء وأخذها في الاعتبار منذ بداية العمل على النظام ، فإن هذا سيحسن بشكل خطير قدرته على التوسع والتوسع.

TDD بطيء


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

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

ويتفاقم الوضع أيضًا من حقيقة أن إنتاجية وكفاءة المبرمجين غالبًا ما يتم تقييمها من حيث سرعة عملهم والوقت الذي يقضونه في إنشاء المنتج النهائي. كل هذا يدفع فريق المبرمجين إلى الرغبة في العمل بأسرع وقت ممكن ، إلى الرغبة في التوافق مع جداول العمل الضيقة.

في هذه الورقة نظام المؤشرات الأربعة لتقييم فعالية DevOps:

  1. تردد النشر.
  2. تغيير وقت التنفيذ.
  3. وقت استرداد الخدمة.
  4. تكرار الفشل مع التغييرات.

أنا مهتم بشكل خاص بالمؤشرين "تكرار النشر" و "تكرار الفشل مع التغييرات".

عند استخدام TDD ، كما قلت ، بناءً على افتراض أن النظام تم إنشاؤه حصريًا باستخدام TDD ، فإن فريق التطوير ، فور إجراء تغيير على النظام ، سيعرف ما إذا كان هذا التغيير لم يؤثر على أداء أي من أجزاء النظام. بالإضافة إلى ذلك ، يزيد استخدام TDD من سرعة العثور على الأخطاء. تسمح لك حقيقة أن الاختبارات مصممة لاختبار أجزاء صغيرة جدًا من التعليمات البرمجية بمعرفة مكان المشروع الذي حدث فيه الفشل بسرعة كبيرة. وهذا يؤدي إلى حقيقة أنه من غير المرجح اكتشاف الأخطاء في إصدار محدث من النظام تم نشره بالفعل ، مما يقلل من معدل "معدل الفشل أثناء التغييرات".

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

يغطي مشروع فينيكس أربعة أنواع من العمل. واحد منهم هو عمل غير متوقع. يجب أن يتم مثل هذا العمل عندما يحتاج المطورون إلى اتخاذ قرار بشأن ما يفعلونه والقيام بشيء آخر. عادة ما يتم تصحيح خطأ ما. إذا تم بناء المشروع من كود الاختبار الذاتي ، فإن هذا يقلل من كمية الأخطاء. وهذا بدوره يؤدي إلى تقليل مقدار "العمل غير المتوقع". وكلما قلت هذه "المفاجآت" في حياة المطور - كلما شعر أنه أفضل وأكثر إنتاجية وأفضل يعمل.

إذا كان المطورون لا يقلقون بشأن الأخطاء في الإنتاج ، فهذا يعني أنه يمكنهم تخصيص المزيد من الوقت لتطوير وظائف مفيدة جديدة للمنتج. وإذا كان المطورون يفكرون في الشفرة مقدمًا ، ويطبقون مبادئ مفيدة مثل SOLID وينخرطون دائمًا في إعادة الهيكلة ، فهذا يقلل من مقدار "الديون الفنية". عندما يكون الفريق واثقًا في وظائف وجودة الشفرة ، يمكنه نشرها حرفياً في عدتين. كل هذا يزيد من سرعة العمل.

ملخص


مثل أي أداة أخرى ، مثل أي طريقة تطوير أخرى ، قد تبدو منهجية TDD محرجة في البداية. وقبل أن يتقن المبرمجون هذه المنهجية بشكل صحيح ، قد يبدو بطيئًا بالنسبة إليهم. ماذا استطيع قوله؟ على حد تعبير Jez Humble: "إذا كان هناك شيء يسبب الانزعاج ، فقم بذلك كثيرًا ويمكنك التعامل معه."

والآن أقترح عليك ممارسة منهجية TDD والقيام بذلك حتى يتوقف TDD عن إزعاجك :)

القراء الأعزاء! هل تستخدم منهجية TDD عند العمل على مشاريعك؟

Source: https://habr.com/ru/post/undefined/


All Articles