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

يعتقد مؤلف المادة ، التي ننشر ترجمتها اليوم ، أن كل هذا كان يمكن تجنبه بفضل TDD.
لماذا أستخدم TDD؟
لم أكن في مثل هذه الحالات لفترة طويلة. وليس الأمر أن المطورين توقفوا عن ارتكاب الأخطاء. والحقيقة هي أنه منذ سنوات عديدة ، في كل فريق أديرته وتأثرت به ، تم تطبيق منهجية TDD. بطبيعة الحال ، لا تزال هناك أخطاء ، ولكن الاختراق في إنتاج المشكلات التي يمكن أن "توقف عن العمل" قد انخفض إلى الصفر تقريبًا ، على الرغم من أن عدد مرات تكرار تحديثات البرامج وعدد المهام التي تحتاج إلى حل أثناء التحديث قد زاد بشكل كبير منذ ذلك الحين عندما حدث شيء تحدثت عنه في البداية.
عندما يسألني أحدهم لماذا يجب عليه الاتصال بشركة TDD ، أقول له هذه القصة ، ويمكنني أن أتذكر أكثر من عشر حالات مماثلة. أحد أهم أسباب تحولي إلى TDD هو أن هذه المنهجية تعمل على
تحسين تغطية الاختبارات بالشفرة ، مما يؤدي إلى
تقليل أخطاء الإنتاج
بنسبة 40 إلى
80٪ . هذا هو أكثر ما يعجبني في TDD. هذا تقلع سلسلة من المشاكل من أكتاف المطورين.
بالإضافة إلى ذلك ، تجدر الإشارة إلى أن TDD ينقذ المطورين من الخوف من إجراء تغييرات على الكود.
في المشروعات التي أشارك فيها ، تمنع مجموعات من الوحدة النمطية والاختبارات الوظيفية يوميًا تقريبا الكود من الدخول إلى الإنتاج ، مما قد يعطل عمل هذه المشروعات بشكل خطير. على سبيل المثال ، أرى الآن 10 تحديثات تلقائية للمكتبة تم إجراؤها الأسبوع الماضي ، مثل قبل إصدارها دون استخدام TDD ، أخشى أن تكون قد أفسدت شيئًا.
تم دمج كل هذه التحديثات تلقائيًا في الشفرة ، وقد تم استخدامها بالفعل في الإنتاج. لم أتحقق من أي منها يدويًا ، ولم أقلق مطلقًا من أنه قد يكون لها تأثير سيء على المشروع. في الوقت نفسه ، من أجل إعطاء هذا المثال ، لم يكن لدي للتفكير لفترة طويلة. لقد فتحت GitHub ، ونظرت إلى عمليات الدمج الأخيرة ، ورأيت ما كنت أتحدث عنه. المهمة التي تم حلها مسبقًا يدويًا (أو الأسوأ من ذلك ، المشكلة التي تم تجاهلها) هي الآن عملية خلفية تلقائية. يمكنك محاولة القيام بشيء مماثل دون تغطية جيدة للشفرة مع الاختبارات ، لكنني لا أوصي بذلك.
ما هو TDD؟
TDD لتقف على اختبار التنمية مدفوعة. العملية المنفذة بتطبيق هذه المنهجية بسيطة للغاية:
اختبارات الكشف عن الأخطاء ، الاختبارات كاملة بنجاح ، يتم تنفيذ إعادة البناءفيما يلي المبادئ الأساسية لاستخدام TDD:
- قبل كتابة رمز تنفيذ لبعض الميزات ، يكتبون اختبارًا يتيح لك التحقق مما إذا كانت شفرة التنفيذ المستقبلية هذه تعمل أم لا. قبل المتابعة إلى الخطوة التالية ، يتم بدء الاختبار واقتناعه بأنه يلقي خطأً. بفضل هذا ، يمكنك التأكد من أن الاختبار لا ينتج نتائج إيجابية خاطئة ، إنه نوع من الاختبارات للاختبارات نفسها.
- أنها تخلق تنفيذ الفرصة والتأكد من اجتيازه للاختبار بنجاح.
- أداء ، إذا لزم الأمر ، إعادة بناء التعليمات البرمجية. إعادة المعالجة ، في وجود اختبار يمكن أن يشير إلى المطور ما إذا كان النظام يعمل بشكل صحيح أو غير صحيح ، يعطي المطور الثقة في تصرفاته.
كيف يمكن أن تساعد TDD في توفير الوقت اللازم لتطوير البرامج؟
للوهلة الأولى ، قد يبدو أن اختبارات الكتابة تعني زيادة كبيرة في حجم رمز المشروع ، وأن كل هذا يتطلب للمطورين الكثير من الوقت الإضافي. في حالتي ، في البداية ، كان كل شيء على ما يرام ، وحاولت أن أفهم كيف ، من حيث المبدأ ، من الممكن كتابة التعليمات البرمجية التي تم اختبارها ، وكيفية إضافة الاختبارات إلى التعليمات البرمجية التي تمت كتابتها بالفعل.
يتميز TDD بمنحنى تعليمي معين ، وبينما يصعد المبتدئين على طول هذا المنحنى ، يمكن أن يزيد الوقت اللازم للتنمية بنسبة
15-35٪ . في كثير من الأحيان هذا هو بالضبط ما يحدث. ولكن في مكان ما بعد حوالي عامين من بدء استخدام TDD ، يبدأ شيء لا يصدق في الحدوث. وهي ، على سبيل المثال ، بدأت ، بالكتابة الأولية لاختبارات الوحدات ، البرمجة بشكل أسرع من ذي قبل عندما لم يتم استخدام TDD.
منذ بضع سنوات ، قمت بتطبيق ، في نظام العميل ، القدرة على العمل مع أجزاء مقطع الفيديو. وهي النقطة التي تكمن في أنه سيكون من الممكن السماح للمستخدم بالإشارة إلى بداية ونهاية جزء التسجيل ، وتلقي ارتباط به ، مما يجعل من الممكن الإشارة إلى مكان محدد في المقطع ، وليس هذا المقطع بأكمله.
لم اكن اعمل وصل اللاعب إلى نهاية الجزء واستمر في تشغيله ، لكن لم يكن لدي أي فكرة عن سبب ذلك.
كنت أحسب أن المشكلة كانت في توصيل مستمعي الأحداث بشكل غير صحيح. بدا الرمز الخاص بي شيئًا مثل هذا:
video.addEventListener('timeupdate', () => { if (video.currentTime >= clip.stopTime) { video.pause(); } });
تبدو عملية العثور على المشكلة على هذا النحو: إجراء تغييرات وتجميع وإعادة التشغيل والنقر والانتظار ... وقد تكررت سلسلة الإجراءات هذه مرارًا وتكرارًا.
من أجل التحقق من كل التغييرات التي أدخلت على المشروع ، استغرق الأمر حوالي دقيقة تقضيها ، وشهدت العديد من الخيارات بشكل لا يصدق لحل المشكلة (معظمها 2-3 مرات).
ربما أكون قد ارتكبت خطأ في الكلمة الأساسية
timeupdate
؟ هل فهمت ميزات العمل مع API بشكل صحيح؟ هل
video.pause()
استدعاء
video.pause()
؟ قمت بإجراء تغييرات على الكود ، وأضاف
console.log()
، وعدت إلى المتصفح ، ونقرت على زر
، ونقرت على الموضع الموجود في نهاية الجزء المحدد ، ثم انتظرت بصبر حتى تم تشغيل المقطع بالكامل. تسجيل الدخول داخل البناء
if
لم يؤد إلى أي شيء. بدا الأمر وكأنه فكرة عن مشكلة محتملة. لقد قمت بنسخ كلمة
timeupdate
من وثائق واجهة برمجة التطبيقات (API) حتى أتأكد تمامًا من أنني لم
timeupdate
عند إدخالها. أقوم بإعادة تحميل الصفحة مرة أخرى ، وانقر مرة أخرى ، وانتظر مرة أخرى. ومرة أخرى ، يرفض البرنامج العمل بشكل صحيح.
أضع أخيرا
console.log()
خارج كتلة
if
. "لن يساعد" ، فكرت. في النهاية ،
if
بسيطة جدًا لدرجة أنه لم يكن لدي أي فكرة عن كيفية تهجئتها بطريقة غير صحيحة. ولكن تسجيل الدخول في هذه الحالة عملت. أنا اختنق القهوة. "ماذا بحق الجحيم هو هذا؟" فكرت.
قانون تصحيح مورفي. مكان البرنامج الذي لم تختبره أبدًا ، نظرًا لأنك تعتقد اعتقادا راسخا أنه لا يمكن أن يحتوي على أخطاء ، سيصبح المكان بالضبط الذي ستجد فيه خطأ بعد ، بعد أن استنفدت بالكامل ، ستجري تغييرات على هذا المكان فقط بسبب أنهم جربوا بالفعل كل ما يمكنهم التفكير فيه.
قمت بتعيين نقطة توقف في البرنامج لفهم ما يحدث. لقد
clip.stopTime
معنى
clip.stopTime
. لدهشتي ، كان
undefined
. لماذا؟ نظرت إلى الكود مرة أخرى. عندما يحدد المستخدم وقت انتهاء الجزء ، يضع البرنامج علامة نهاية القطعة في المكان المناسب ، لكن لا يقوم بتعيين قيمة
clip.stopTime
. اعتقدت "أنا غبي لا يصدق ، يجب ألا يُسمح لي بدخول أجهزة الكمبيوتر حتى نهاية حياتي".
أنا لم أنسى هذا وبعد سنوات. وكل ذلك - بفضل الإحساس الذي مررت به ، ما زلت أجد خطأً. ربما تعرف ما أتحدث عنه. مع كل هذا حدث. وربما ، سيتمكن الجميع من التعرف على أنفسهم في هذه الميم.
هذه هي الطريقة التي أبدو فيها عندما أبرمجإذا كتبت هذا البرنامج اليوم ، فسأبدأ العمل عليه مثل هذا:
describe('clipReducer/setClipStopTime', async assert => { const stopTime = 5; const clipState = { startTime: 2, stopTime: Infinity }; assert({ given: 'clip stop time', should: 'set clip stop time in state', actual: clipReducer(clipState, setClipStopTime(stopTime)), expected: { ...clipState, stopTime } }); });
هناك شعور بوجود كود أكثر بكثير من هذا السطر:
clip.stopTime = video.currentTime
لكن هذا هو بيت القصيد. هذا الرمز بمثابة مواصفات. هذا هو كلا من الوثائق وإثبات أن الرمز يعمل كما هو مطلوب في هذه الوثائق. ونظرًا لوجود هذه الوثائق ، إذا قمت بتغيير ترتيب العمل مع علامة وقت انتهاء جزء ما ، فلا داعي للقلق بشأن ما إذا كنت قد انتهكت العملية الصحيحة أثناء إدخال هذه التغييرات مع وقت انتهاء المقطع ، أثناء إدخال هذه التغييرات.
هنا ، بالمناسبة ، مادة مفيدة لكتابة اختبارات الوحدة ، مثل تلك التي درسناها للتو.
النقطة ليست كم من الوقت يستغرق لإدخال هذا الرمز. النقطة الأساسية هي كم من الوقت يستغرق التصحيح إذا حدث خطأ ما. إذا كان الرمز غير صحيح ، فسيقوم الاختبار بتقديم تقرير خطأ ممتاز. سأعلم على الفور أن المشكلة ليست هي معالج الأحداث.
setClipStopTime()
أنه إما في
setClipStopTime()
أو في
clipReducer()
، حيث يتم تطبيق تغيير الحالة. بفضل الاختبار ، أود أن أعرف ما هي الوظائف التي تؤديها الشفرة وما الذي تعرضه بالفعل وما هو متوقع منها. والأهم من ذلك ، سيكون لزميلي المعرفة نفسها ، والذي بعد ستة أشهر من كتابة الكود ، سوف يقدم ميزات جديدة فيه.
ببدء مشروع جديد ، كأحد الأشياء الأولى ، قمت بإعداد برنامج
نصي للمراقب يقوم تلقائيًا بإجراء اختبارات وحدة في كل مرة يتم فيها تغيير ملف معين. أنا البرنامج في كثير من الأحيان باستخدام شاشتين. على أحدهما ، يتم فتح وحدة التحكم الخاصة بالمطور ، حيث يتم عرض نتائج هذا البرنامج النصي ، من ناحية أخرى ، يتم عرض واجهة البيئة التي أكتب فيها الرمز. عندما أقوم بإجراء تغيير على الشفرة ، عادةً ما ، خلال 3 ثوانٍ ، اكتشف ما إذا كان التغيير قد أصبح يعمل أم لا.
بالنسبة لي ، TDD هو أكثر بكثير من مجرد التأمين. هذه هي القدرة على تلقي المعلومات بسرعة عن حالة الكود الخاص بي باستمرار وبسرعة. مكافأة فورية في شكل اختبارات مرت ، أو تقرير فوري عن الأخطاء في حالة قيامي بشيء خاطئ.
كيف علمت منهجية TDD كيفية كتابة كود أفضل؟
أرغب في تقديم قبول واحد ، حتى أعترف أنه أمر محرج: لم يكن لدي أي فكرة عن كيفية إنشاء تطبيقات قبل أن أتعلم TDD واختبار الوحدة. لا أستطيع أن أتخيل كيف تم تعييني على الإطلاق ، ولكن بعد إجراء مقابلات مع مئات المطورين ، يمكنني أن أقول بثقة أن العديد من المبرمجين في وضع مماثل. لقد علمتني منهجية TDD كل شيء أعرفه تقريبًا حول التحلل والتكوين الفعالين لمكونات البرنامج (أعني الوحدات ، الوظائف ، الكائنات ، مكونات واجهة المستخدم ، إلخ).
والسبب في ذلك هو أن اختبارات الوحدة تجبر المبرمج على اختبار المكونات بمعزل عن بعضها البعض ومن الأنظمة الفرعية للإدخال / الإخراج. إذا تم تزويد الوحدة ببعض بيانات المدخلات ، فيجب عليها إعطاء بيانات مخرجات معينة كانت معروفة سابقًا. إذا لم يفعل ، فشل الاختبار. إذا نجح ذلك ، ينجح الاختبار. النقطة هنا هي أن الوحدة يجب أن تعمل بشكل مستقل عن بقية التطبيق. إذا كنت تختبر منطق الحالة ، فيجب أن تكون قادرًا على القيام بذلك دون عرض أي شيء على الشاشة أو حفظ أي شيء في قاعدة البيانات. إذا كنت تختبر تشكيل واجهة المستخدم ، فيجب أن تكون قادرًا على اختبارها دون الحاجة إلى تحميل الصفحة في مستعرض أو الوصول إلى موارد الشبكة.
من بين أشياء أخرى ، علمني منهجية TDD أن الحياة تصبح أسهل كثيرًا إذا كنت تسعى إلى الحد الأدنى عند تطوير مكونات واجهة المستخدم. بالإضافة إلى ذلك ، يجب عزل منطق الأعمال والآثار الجانبية عن واجهة المستخدم. من وجهة نظر عملية ، هذا يعني أنه إذا كنت تستخدم إطار عمل لواجهة المستخدم المستندة إلى مكون مثل
React أو Angular ، فقد يكون من المجدي إنشاء مكونات عرض تقديمي تكون مسؤولة عن عرض شيء ما على الشاشة ومكونات الحاوية غير المتصلة ببعضها البعض مختلطة.
يقوم مكون العرض التقديمي الذي يستقبل خصائص معينة دائمًا بإنشاء نفس النتيجة. يمكن التحقق من هذه المكونات بسهولة باستخدام اختبارات الوحدة. يتيح لك ذلك معرفة ما إذا كان المكون يعمل بشكل صحيح مع الخصائص وما إذا كان المنطق الشرطي المعين المستخدم في تكوين الواجهة صحيحًا أم لا. على سبيل المثال ، من الممكن ألا يعرض المكون الذي يتكون من القائمة أي شيء آخر غير دعوة لإضافة عنصر جديد إلى القائمة إذا كانت القائمة فارغة.
كنت أعرف مبدأ الفصل بين المسؤوليات قبل وقت طويل من إتقان TDD ، لكنني لم أكن أعرف كيفية تقاسم المسؤولية بين الكيانات المختلفة.
لقد سمح لي اختبار الوحدة بدراسة استخدام mokas لاختبار شيء ما ، ثم اكتشفت أن تناول الطعام هو علامة على أن
هناك شيئًا ما قد يكون خطأ في الشفرة . فاجأني وغيرت نهجي تمامًا لتكوين البرنامج.
كل تطوير البرمجيات عبارة عن تركيبة: عملية تقسيم المشكلات الكبيرة إلى العديد من المشكلات الصغيرة التي يمكن حلها بسهولة ، ثم إنشاء حلول لهذه المشكلات التي تشكل التطبيق. يشير التكسير من أجل اختبارات الوحدة إلى أن الوحدات الذرية للتكوين ليست في الحقيقة ذرية. سمحت لي دراسة كيفية التخلص من mok دون التأثير على تغطية الشفرة من خلال الاختبارات لمعرفة كيفية تحديد الأسباب الخفية التي لا حصر لها للترابط القوي للكيانات.
هذا سمح لي ، كمطور ، بالنمو بشكل احترافي. علمني هذا كيفية كتابة رمز أبسط كثيرًا من السهل تمديده وصيانته وحجمه. وهذا ينطبق على تعقيد الكود نفسه ، وعلى تنظيم أعماله في الأنظمة الموزعة الكبيرة مثل البنى التحتية السحابية.
كيف يوفر TDD وقت الفريق؟
لقد قلت بالفعل أن TDD ، في المقام الأول ، يؤدي إلى
تحسين تغطية الشفرة مع الاختبارات. السبب في ذلك هو أننا لا نبدأ في كتابة التعليمات البرمجية لتنفيذ بعض الميزات حتى نكتب اختبارًا يتحقق من التشغيل الصحيح لهذا الرمز في المستقبل. أولا نكتب اختبار. ثم نسمح لها أن تنتهي بخطأ ما. ثم نكتب رمز لتنفيذ الفرصة. نحن نختبر الشفرة ، نتلقى رسالة خطأ ، نحقق النجاح الصحيح للاختبارات ، ونقوم بإعادة بناء وتجديد هذه العملية.
تسمح لك هذه العملية بإنشاء "سياج" يمكن من خلاله "القفز" من خلال أخطاء قليلة فقط. حماية الأخطاء هذه لها تأثير مدهش على فريق التطوير بأكمله. أنه يخفف الخوف من فريق الدمج.
يسمح المستوى العالي من تغطية الشفرة مع الاختبارات للفريق بالتخلص من الرغبة في التحكم يدويًا في أي تغيير ، حتى صغير ، في قاعدة الشفرة. تصبح تغييرات التعليمات البرمجية جزءًا طبيعيًا من سير العمل.
إن التخلص من الخوف من إجراء تغييرات على الكود يشبه عدم وضوح جهاز معين. إذا لم يتم ذلك ، فسيتوقف الجهاز في النهاية - حتى يتم تشحيمه وإعادة تشغيله.
بدون هذا الخوف ، تصبح عملية العمل على البرامج أكثر هدوءًا من ذي قبل. لا يتم تأخير طلبات السحب حتى آخرها. سيقوم نظام CI / CD بإجراء الاختبارات ، وإذا فشلت الاختبارات ، فستوقف عملية إجراء تغييرات على رمز المشروع. في الوقت نفسه ، سيكون من الصعب للغاية عدم إشعار رسائل الخطأ والمعلومات حول مكان حدوثها بالضبط.
هذه هي بيت القصيد.
أعزائي القراء! هل تستخدم TDD عند العمل في مشاريعك؟
