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

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

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

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

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

أصعب شيء في البرمجة التي تعرفها. وفي الاختبارات ، هذا مهم حقًا. إذا لم تقم بتسمية الاختبار جيدًا ، فمن المحتمل أنك لن تتمكن من صياغة ما يتحقق منه الاختبار.
البشر مخلوقات بسيطة إلى حد ما ، وهم محاصرون بسهولة في الأسماء. لذلك ، أطلب منك استدعاء الاختبارات جيدًا. قم بصياغة اختبار للتحقق من القواعد البسيطة واتباعها.
no_action_or_assertion
إذا كان اسم الاختبار لا يحتوي على وصف لما يتحقق الاختبار ، على سبيل المثال ، لديك فئة وحدة التحكم وتكتب اختبار testController ، فما الذي تحققه؟ ماذا يجب أن تفعل هذا الاختبار؟ على الأرجح ، لا شيء أو أشياء كثيرة للغاية للتحقق. لا أحد ولا الآخر يناسبنا. لذلك ، من الضروري أن تكتب باسم الاختبار ما نقوم بفحصه.
اسم طويل
لا يمكنك الذهاب إلى الطرف الآخر. يجب أن يكون اسم الاختبار قصيرًا بدرجة كافية حتى يتمكن الشخص من تحليله بسهولة. بهذا المعنى ، تعد Kotlin رائعة لأنها تتيح لك كتابة أسماء اختبار بعلامات اقتباس مع مسافات باللغة الإنجليزية العادية. فهي أسهل في القراءة. ولكن لا يزال ، أسماء طويلة رائحة.
إذا كان اسم الاختبار طويلًا جدًا ، فمن المرجح أنك وضعت العديد من طرق الاختبار في فصل اختبار واحد ، وتحتاج إلى توضيح ما تقوم بفحصه. في هذه الحالة ، تحتاج إلى تقسيم فصل الاختبار الخاص بك إلى عدة. لا حاجة للخوف من هذا. سيكون لديك اسم فئة اختبار يتحقق من اسم رمز الإنتاج الخاص بك ، وسيكون هناك أسماء اختبار قصيرة.
senior_prefix
هذا هو atavism. في السابق ، في Java ، تم اختبار كل شخص باستخدام JUnit ، حيث كان هناك حتى الإصدار الرابع اتفاق على أن طرق الاختبار يجب أن تبدأ باختبار الكلمة. حدث ذلك ، ما زال الجميع يسميها. ولكن هناك مشكلة ، في اللغة الإنجليزية اختبار الكلمة هو الفعل "الاختيار". يتم اكتشاف الناس بسهولة في هذا الفخ ، ولم يعد يكتب أي أفعال أخرى. اكتب testController. من السهل التحقق من نفسك: إذا لم تكتب فعلًا لما يجب أن يفعله فصل الاختبار الخاص بك ، فعلى الأرجح أنك لم تقم بفحص شيء ما ، فلن تكتبه جيدًا بما فيه الكفاية. لذلك ، أطلب منك دائمًا إزالة اختبار الكلمة من أسماء طرق الاختبار.
أقول أشياء بسيطة للغاية ، لكن الغريب أنها تساعد. إذا تم استدعاء الاختبارات جيدًا ، فمن المرجح أن تبدو جيدة تحت الغطاء. انها بسيطة جدا.

أنا في الواقع قراءة اختبار رائحة معرفات كما هو الحال في جيثب. الرابط أدناه ، يمكنك المشي والاستخدام.
multi_asserts
في طريقة الاختبار هناك العديد من التأكيدات. إذن ربما أم لا؟ ربما. هل هو جيد أم سيء؟ أعتقد أن هذا سيء للغاية. إذا كتبت عدة تأكيدات في طريقة اختبار ، فتقوم بفحص عدة عبارات. إذا قمت باختبار الاختبار وسقط التأكيد الأول ، فهل سيصل الاختبار إلى التأكيد الثاني؟ لن تصل. أنت بالفعل بعد سقوط التجميع الخاص بك في مكان ما على CI تحصل على أن الاختبار قد سقط ، اذهب واصلح شيئًا ما ، املأه مرة أخرى ، وسوف يقع في التأكيد التالي. يمكن أن يكون جيدا جدا.
في هذه الحالة ، سيكون أكثر برودة إذا رأيت طريقة الاختبار هذه في عدة طرق ، وجميع الطرق التي تحتوي على تأكيدات عديدة سقطت في نفس الوقت ، لأنه سيتم إطلاقها بشكل مستقل عن بعضها البعض.
يمكن لبضعة تأكيدات أخرى إخفاء الإجراءات المختلفة التي يتم تنفيذها مع فئة الاختبار. أوصي كتابة اختبار واحد - تأكيد واحد. في الوقت نفسه ، يمكن أن تكون التأكيدات معقدة للغاية. أظهر زميلي ، في التقرير الأول ، قطعة من الشفرة حيث استخدم التأكيد الممتاز على هذا البناء والمطابق. أنا حقًا أحب التنافس في JUnit ، حتى تتمكن من استخدام ذلك أيضًا. بالنسبة لقارئ الاختبار ، اتضح أنه عبارة قصيرة واحدة فقط. لدى GitHub أمثلة على كل هذه الروائح وكيفية إصلاحها. هناك مثال على الكود السيئ وبعض الكود الجيد. يتم كل ذلك في شكل مشروع يمكنك تنزيل جميع الاختبارات وفتحه وتجميعه وتشغيله.
many_tests_in_one
ترتبط الرائحة التالية ارتباطًا وثيقًا بالرائحة السابقة. كنت تفعل شيئا مع النظام - يمكنك تأكيد. القيام بشيء آخر مع النظام ، وبعض العمليات الطويلة - القيام بتأكيد - القيام بشيء آخر. في الواقع ، لقد رأيت ببساطة عدة طرق ، ويمكنك الحصول على طرق اختبار جيدة وجيدة.
repeating_setup
هذا يشير إلى الإلحاح. إذا كان لديك فصل اختبار ، وتدير كل طريقة اختبار نفس الطرق في البداية.
فئة اختبار يتم فيها تنفيذ نفس الطرق في البداية. يبدو هذا قليلاً ، لكن في كل طريقة اختبار توجد هذه القمامة. وإذا كانت هذه الطريقة شائعة في جميع طرق الاختبار ، فلماذا لا تأخذها في المُنشئ أو
قبل الكتلة أو
قبل كل كتلة في JUnit 5. إذا قمت بذلك ، فسوف تتحسن قابلية قراءة كل طريقة ، بالإضافة إلى أنك ستتخلص من DRY sin. مثل هذه الاختبارات أسهل في الصيانة وأسهل في القراءة.

موثوقية الاختبارات مهمة جدا. هناك علامات يمكن من خلالها تحديد أن الاختبار سيبكي ، أخضر أو أحمر. عندما يكتب المطور ذلك ، فهو متأكد من أنه أخضر ، ثم لسبب ما أن تصبح الاختبارات خضراء أو حمراء ، مما يعطينا الألم والشك بشكل عام بأن الاختبارات مفيدة. لسنا متأكدين من الاختبارات ، مما يعني أننا لسنا متأكدين من أنها مفيدة.
عشوائي
أنا شخصياً كتبت اختبارات ذات Math.random () بالداخل ، وقمت بأرقام عشوائية ، وفعلت شيئًا معهم. لا حاجة للقيام بذلك. نتوقع أن يدخل نظام الاختبار في نظام الاختبار بنفس التكوين ، كما يجب أن يكون الناتج منه هو نفسه. لذلك ، في اختبارات الوحدة ، على سبيل المثال ، لن تحتاج أبدًا إلى القيام بأي عمليات مع الشبكة. نظرًا لأن الخادم قد لا يستجيب ، فقد يكون هناك توقيتات مختلفة ، شيء آخر.
إذا كنت بحاجة إلى اختبار يعمل مع الشبكة ، فقم بإجراء وكيل ، محلي ، أي شيء ، ولكن لا يمكنك بأي حال من الأحوال الانتقال إلى شبكة حقيقية. هذا هو نفس عشوائي. وبالطبع ، لا يمكنك استخدام بيانات عشوائية. إذا كان عليك القيام بشيء ما ، فافعل بعض الأمثلة بشروط حدية ، وظروف سيئة ، ولكن يجب أن تكون مضغوطة.
tread_sleep
مشكلة كلاسيكية تواجه المطورين عند محاولة اختبار نوع من التعليمات البرمجية غير المتزامنة. لقد فعلت شيئًا في الاختبار ، ثم أحتاج إلى الانتظار حتى اكتماله. كيف تصنع؟ Thread.sleep () ، بالطبع.
هناك مشكلة. عندما تقوم بتطوير الاختبار ، على سبيل المثال ، قمت بذلك على بعض آلة كاتبة الخاص بك ، فإنه يعمل بسرعة. يمكنك تشغيل الاختبارات على جهاز آخر. وماذا سيحدث إذا لم ينجح نظامك في العمل خلال وقت Thread.sleep ()؟ الاختبار يتحول إلى اللون الأحمر. هذا غير متوقع. لذلك ، التوصية هنا هي ، إذا كنت تقوم بعمليات غير متزامنة ، لا تختبرها على الإطلاق. يمكن نشر أي عملية غير متزامنة تقريبًا بحيث يكون لديك نوع من الآلية الشرطية التي توفر عملية غير متزامنة وكتلة تعليمات برمجية منفذة بشكل متزامن. على سبيل المثال ، يحتوي AsyncTask من الداخل على كتلة من التعليمات البرمجية يتم تنفيذها بشكل متزامن. يمكنك اختباره بسهولة بشكل متزامن ، دون أي تزامن. ليست هناك حاجة لاختبار AsyncTask نفسه ، بل هو فئة إطار ، لماذا اختباره؟ بين قوسين وستكون حياتك أسهل.
Thread.sleep () هو الكثير من الألم. بالإضافة إلى حقيقة أنه يزيد من موثوقية الاختبارات ، لأنه يسمح لهم بالبكاء بسبب توقيتات مختلفة على الأجهزة ، فإنه يؤدي أيضًا إلى إبطاء تنفيذ الاختبارات. من الذي يرغب في إجراء اختبارات وحدته ، والتي يجب إجراؤها بالمللي ثانية ، لمدة خمس ثوان ، لأنني أقضي وقت النوم؟
edit_global
من الرائحة النموذجية أننا قمنا بتغيير بعض المتغيرات الثابتة العالمية في بداية الاختبار للتحقق من أن نظامنا يعمل بشكل صحيح ، لكنه لم يعد في النهاية. بعد ذلك ، حصلنا على موقف رائع: على الجهاز ، أجرى المطور الاختبارات في تسلسل واحد ، أولاً فحص المتغير الشامل بالقيمة الافتراضية ، ثم في الاختبار قام بتغييره ، ثم قام بشيء آخر. كلا الاختبارين أخضر. وعلى CI ، حدث ذلك ، بدأت الاختبارات بالترتيب العكسي. وسيكون أحد الاختبارين أو كليهما أحمر ، على الرغم من أنها كانت جميعها خضراء.
تحتاج إلى تنظيف بعد نفسك. القواعد الكشفية بهذا المعنى: غيرت المتغير الشامل - ارجع إلى حالته الأصلية. الأفضل من ذلك ، تأكد من عدم استخدام الدول العالمية. لكن هذا تفكير أعمق. إنها تدور حول حقيقة أن الاختبارات تبرز أحيانًا العيوب في الهندسة المعمارية. إذا كان يتعين علينا تغيير الدول العالمية وإعادتها إلى حالتها الأصلية من أجل كتابة الاختبارات ، فهل نحن جميعًا نحقق نتائج جيدة في بنياننا؟ هل نحن حقا بحاجة إلى متغيرات عالمية ، على سبيل المثال؟ كقاعدة عامة ، يمكنك الاستغناء عنها عن طريق حقن بعض فئات السياقات أو أي شيء ، بحيث يمكنك إعادة تكوينها وحقنها وإعادة تهيئتها في كل مرة في الاختبار.
تضمين التغريدة
اختبار رائحة المتقدمة. لا تنشأ الحاجة إلى استخدام مثل هذا الشيء في اليوم الأول ، كقاعدة عامة. لقد اختبرت بالفعل شيئًا ما ، ثم كنت بحاجة لترجمة الفصل الدراسي إلى حالة معينة. وأنت تجعل نفسك مستتر. لديك فئة إنتاج ، وتقوم بإجراء طريقة محددة لن يتم استدعاءها أبدًا في الإنتاج ، ومن خلالها تقوم بضخ شيء ما في الفصل أو تغيير حالته. وهكذا ، كسر التغليف الخبيثة. في الإنتاج ، يعمل فصلك بطريقة أو بأخرى ، ولكن في الاختبارات ، في الواقع ، إنها فئة مختلفة ، يمكنك التواصل معها من خلال المدخلات والمخرجات الأخرى. وهنا يمكنك الحصول على موقف يمكنك فيه تغيير الإنتاج ، لكن الاختبارات لا تلاحظ ذلك. تستمر الاختبارات في اجتياز الباب الخلفي ولم تلاحظ أنه على سبيل المثال ، بدأت الاستثناءات في إطلاق النار في المنشئ ، لأنها تمر عبر مُنشئ آخر.
بشكل عام ، يجب عليك اختبار الفصول الدراسية من خلال نفس المدخلات والمخرجات كما هو الحال في الإنتاج. لا ينبغي أن يكون هناك وصول إلى أي طرق للاختبارات فقط.

كم من 15 ألف اختبار يتم إجراؤها؟ حوالي 20 دقيقة ، بناءً على كل طلب تجمع ، على Team City ، يضطر المطورين إلى الانتظار. فقط لأن 15 ألف هو الكثير من الاختبارات. وفي هذا القسم ، قمت بتجميع الروائح التي تبطئ الاختبارات. على الرغم من أن thread_sleep كان بالفعل هناك.
لا لزوم لها
يحتوي Android على اختبارات الأجهزة ، فهي جميلة ، وهي تعمل على جهاز أو محاكي. سيؤدي هذا إلى رفع مشروعك تمامًا ، حقيقي ، لكنه بطيء جدًا. وبالنسبة لهم تحتاج إلى رفع محاكي كامل. حتى إذا كنت تتخيل أن لديك محاكيًا مرفوعًا على CI - إنه يتزامن تمامًا مع امتلاكك له - فسوف يستغرق تشغيل الاختبار على المحاكي وقتًا أطول بكثير من الجهاز المضيف ، على سبيل المثال ، باستخدام Robolectric. رغم أن هناك طرق أخرى. هذا هو الإطار الذي يسمح لك بالعمل مع فئات من إطار عمل Android على الجهاز المضيف ، في Java الخالص. نستخدمها بنشاط كبير. في السابق ، كانت Google رائعة إلى حد ما حول هذا الموضوع ، ولكن الآن ، يتحدث موظفو Google أنفسهم عن ذلك في تقارير مختلفة ، يوصى باستخدامه.
لا لزوم لها
يحاكي إطار أندرويد Robolectric. أنها ليست كاملة هناك ، على الرغم من أن التنفيذ أبعد ، أكمل. إنه Android حقيقي تقريبًا ، يعمل فقط على سطح المكتب أو الكمبيوتر المحمول أو CI. لكنه أيضًا لا يحتاج إلى استخدامه في كل مكان. Robolectric ليست حرة. إذا كان لديك اختبار قمت بنقله بشكل بطولي من أجهزة Android إلى Robolectric ، فيجب أن تفكر - ربما تذهب أبعد من ذلك ، تخلص من Robolectric ، قم بتحويله إلى أبسط اختبار JUnit؟ تستغرق اختبارات Robolectric وقتًا في التهيئة ومحاولة تحميل الموارد وتهيئة نشاطك والتطبيق وكل شيء آخر. يستغرق بعض الوقت. هذه ليست ثانية ، إنها ملي ثانية ، وأحيانًا عشرات ومئات. ولكن عندما يكون هناك العديد من الاختبارات ، حتى هذا مهم.
هناك تقنيات تتخلص من Robolectric. يمكنك عزل الشفرة الخاصة بك عبر واجهات من خلال لف جزء النظام الأساسي بأكمله بواجهات. ثم سيكون هناك مجرد اختبار المضيف JUnit. JUnit على الجهاز المضيف سريع جدًا ، يوجد قدر ضئيل من النفقات العامة ، يمكن إجراء مثل هذه الاختبارات بالآلاف وعشرات الآلاف ، وسوف تعمل لمدة دقيقة أو بضع دقائق. لسوء الحظ ، تعمل اختباراتنا لفترة طويلة لأن لدينا الكثير من اختبارات أجهزة Android ، لأن لدينا جزءًا أصليًا في المتصفح ، ونحن مضطرون لتشغيلها على محاكي أو جهاز حقيقي. لماذا وقت طويل.
لن أتحمل لك بعد الآن. كم عدد الروائح التي لديك؟ حتى الآن ، سبعة كحد أقصى. اشترك في
القناة ، ضع النجوم.
