أفضل ممارسات التطوير القائم على الاختبار باستخدام C# وRhinoMocks [مغلق]

StackOverflow https://stackoverflow.com/questions/124210

سؤال

من أجل مساعدة فريقي على كتابة تعليمات برمجية قابلة للاختبار، توصلت إلى هذه القائمة البسيطة لأفضل الممارسات لجعل قاعدة تعليمات برمجية C# الخاصة بنا أكثر قابلية للاختبار.(تشير بعض النقاط إلى القيود المفروضة على Rhino Mocks، وهو إطار عمل ساخر لـ C#، ولكن القواعد قد تنطبق بشكل عام أيضًا.) هل يتبع أي شخص أي أفضل الممارسات؟

لتحقيق أقصى قدر من قابلية اختبار التعليمات البرمجية، اتبع القواعد التالية:

  1. اكتب الاختبار أولاً، ثم الكود. سبب:يضمن ذلك أنك تكتب تعليمات برمجية قابلة للاختبار وأن كل سطر من التعليمات البرمجية يخضع لاختبارات مكتوبة له.

  2. فئات التصميم باستخدام حقن التبعية. سبب:لا يمكنك السخرية أو اختبار ما لا يمكن رؤيته.

  3. افصل رمز واجهة المستخدم عن سلوكه باستخدام Model-View-Controller أو Model-View-Presenter. سبب:يسمح باختبار منطق الأعمال بينما يتم تصغير الأجزاء التي لا يمكن اختبارها (واجهة المستخدم).

  4. لا تكتب أساليب أو فئات ثابتة. سبب:من الصعب أو من المستحيل عزل الأساليب الثابتة ولا يستطيع Rhino Mocks الاستهزاء بها.

  5. البرنامج خارج الواجهات، وليس الطبقات. سبب:يؤدي استخدام الواجهات إلى توضيح العلاقات بين الكائنات.يجب أن تحدد الواجهة الخدمة التي يحتاجها الكائن من بيئته.كما يمكن الاستهزاء بالواجهات بسهولة باستخدام Rhino Mocks وأطر المحاكاة الأخرى.

  6. عزل التبعيات الخارجية. سبب:لا يمكن اختبار التبعيات الخارجية التي لم يتم حلها.

  7. قم بوضع علامة افتراضية على الأساليب التي تنوي الاستهزاء بها. سبب:Rhino Mocks غير قادر على الاستهزاء بالطرق غير الافتراضية.

هل كانت مفيدة؟

المحلول

بالتأكيد قائمة جيدة.وهنا بعض الأفكار حول هذا الموضوع:

اكتب الاختبار أولاً، ثم الكود.

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

فئات التصميم باستخدام حقن التبعية.

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

افصل رمز واجهة المستخدم عن سلوكه باستخدام Model-View-Controller أو Model-View-Presenter.

متفق.لاحظ أنه حتى مقدم العرض/وحدة التحكم يمكن اختباره باستخدام DI/IoC، من خلال تسليمه عرضًا ونموذجًا متعثرًا/مسخرًا.الدفع المقدم أولا TDD للمزيد عن ذلك.

لا تكتب أساليب أو فئات ثابتة.

لست متأكدا من أنني أتفق مع هذا واحد.من الممكن اختبار الوحدة لأسلوب/فئة ثابتة دون استخدام نماذج وهمية.لذا، ربما تكون هذه إحدى قواعد Rhino Mock المحددة التي ذكرتها.

البرنامج خارج الواجهات، وليس الطبقات.

أتفق معك، ولكن لسبب مختلف قليلاً.توفر الواجهات قدرًا كبيرًا من المرونة لمطور البرامج - بما يتجاوز مجرد دعم أطر عمل الكائنات الوهمية المتنوعة.على سبيل المثال، ليس من الممكن دعم DI بشكل صحيح بدون واجهات.

عزل التبعيات الخارجية.

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

قم بوضع علامة افتراضية على الأساليب التي تنوي الاستهزاء بها.

هذا هو الحد من وحيد القرن Mocks.في بيئة تفضل بذرة مشفرة يدويًا على إطار عمل كائن وهمي، لن يكون ذلك ضروريًا.

وهناك نقطتان جديدتان يجب مراعاتهما:

استخدم أنماط التصميم الإبداعي. سيساعد هذا في DI، لكنه يسمح لك أيضًا بعزل هذا الرمز واختباره بشكل مستقل عن المنطق الآخر.

كتابة الاختبارات باستخدام تقنية الترتيب/الفعل/التأكيد لبيل ويك. توضح هذه التقنية تمامًا ما هو التكوين الضروري، وما الذي يتم اختباره بالفعل، وما هو المتوقع.

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

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

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

نصائح أخرى

إذا كنت تعمل مع ‎.Net 3.5، فقد ترغب في الاطلاع على موك المكتبة الساخرة - تستخدم أشجار التعبير واللامدا لإزالة لغة الرد على التسجيل غير البديهية لمعظم المكتبات الساخرة الأخرى.

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

// ShouldExpectMethodCallWithVariable
int value = 5;
var mock = new Mock<IFoo>();

mock.Expect(x => x.Duplicate(value)).Returns(() => value * 2);

Assert.AreEqual(value * 2, mock.Object.Duplicate(value));

اعرف الفرق بين مزيفة، يسخر وبذرة ومتى يستخدم كل منهما.

تجنب الإفراط في تحديد التفاعلات باستخدام المحاكاة.وهذا يجعل الاختبارات هش.

هذه هي وظيفة مفيدة جدا!

أود أن أضيف أنه من المهم دائمًا فهم السياق والنظام قيد الاختبار (SUT).يعد اتباع مبادئ TDD حرفيًا أسهل بكثير عندما تكتب تعليمات برمجية جديدة في بيئة تتبع فيها التعليمات البرمجية الموجودة نفس المبادئ.ولكن عندما تكتب تعليمات برمجية جديدة في بيئة غير قديمة بـ TDD، تجد أن جهود TDD الخاصة بك يمكن أن تتضخم بسرعة إلى ما هو أبعد من تقديراتك وتوقعاتك.

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

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

أميل إلى الاعتقاد بأن القيمة الأساسية لـ TDD تكمن في الحدود (الصندوق الأسود) وكذلك في اختبار الصندوق الأبيض العرضي للمناطق ذات المهام الحرجة في النظام.

السبب الحقيقي وراء البرمجة ضد الواجهات ليس تسهيل حياة Rhino، بل توضيح العلاقات بين الكائنات في الكود.يجب أن تحدد الواجهة الخدمة التي يحتاجها الكائن من بيئته.يوفر الفصل تطبيقًا معينًا لتلك الخدمة.اقرأ كتاب "تصميم الكائنات" لريبيكا ويرفس-بروك عن الأدوار والمسؤوليات والمتعاونين.

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

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

إليك شيء آخر فكرت فيه وأحب القيام به.

إذا كنت تخطط لإجراء اختبارات من وحدة اختبار واجهة المستخدم الرسومية بدلاً من TestDriven.Net أو NAnt، فقد وجدت أنه من الأسهل تعيين نوع مشروع اختبار الوحدة على تطبيق وحدة التحكم بدلاً من المكتبة.يتيح لك هذا إجراء الاختبارات يدويًا والتنقل خلالها في وضع التصحيح (وهو ما يمكن لـ TestDriven.Net المذكور أعلاه القيام به بالفعل من أجلك).

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top