[c#] جعل الطريقة الخاصة العامة للوحدة اختبارها ... فكرة جيدة؟



Answers

أنا شخصياً ، أفضّل اختبار الوحدة باستخدام واجهة برمجة التطبيقات (API) العامة ، ومن المؤكد أنني لن أجعل الطريقة العامة عامة فقط لتجعل من السهل اختبارها.

إذا كنت تريد اختبار الطريقة الخاصة بشكل منعزل ، في Java يمكنك استخدام Easymock / Powermock للسماح لك بالقيام بذلك.

يجب أن تكون عمليًا بشأن ذلك ويجب أن تكون مدركًا للأسباب التي تجعل من الصعب اختبار الأشياء.

" استمع إلى الاختبارات " - إذا كان من الصعب اختبارها ، فهل هذا يخبرك بشيء عن تصميمك؟ هل يمكن أن تفسر إلى أين سيكون اختبار هذه الطريقة تافهاً ويمكن تغطيته بسهولة عن طريق الاختبار من خلال واجهة المستخدم العامة؟

في ما يلي ما قاله Michael Feathers في " العمل بفاعلية مع قانون التراث "

"يقضي العديد من الأشخاص وقتًا طويلاً في محاولة اكتشاف كيفية التغلب على هذه المشكلة ... الإجابة الحقيقية هي أنه إذا كان لديك الرغبة في اختبار طريقة خاصة ، فلا ينبغي أن تكون هذه الطريقة خاصة ؛ إذا تم جعل الطريقة عامة يزعجك ، احتمالات ، لأنه جزء من reponcibility منفصلة ؛ يجب أن يكون على فئة أخرى. " [ العمل الفعال مع قانون تراث (2005) من قبل M. الريش]

Question

مشرف المناقشة: يوجد بالفعل 39 إجابات تم نشرها هنا (تم حذف بعضها). قبل نشر إجابتك ، ضع في اعتبارك ما إذا كان بإمكانك إضافة شيء ذي معنى للمناقشة أم لا. أنت أكثر من مجرد تكرار ما قاله شخص آخر بالفعل.

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

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

هل يجد الآخرون أنفسهم يفعلون ذلك ، لأنني لا أحب فعل ذلك؟ أنا شخصياً أعتقد أن المكافآت تفوق مشاكل جعل الطريقة عامة ولا تقدم أي خدمة خارج الفصل ...

تحديث

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

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




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

ألق نظرة على هذا الفيديو للحصول على لقطة مثيرة للاهتمام حول هذا الموضوع.




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

بدلا من ذلك يجب عليك "اختبار أعلى". اختبر الطريقة التي تستدعي الطريقة الخاصة. لكن يجب أن تختبر اختباراتك احتياجات التطبيقات الخاصة بك وليس "قرارات التنفيذ" الخاصة بك.

على سبيل المثال (رمز pseudo bod هنا) ؛

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

لا يوجد سبب لاختبار "إضافة" يمكنك اختبار "الكتب" بدلاً من ذلك.

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




I usually leave those methods as protected and place the unit test within the same package (but in another project or source folder), where they can access all the protected methods because the class loader will place them within the same namespace.




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




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

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

هذا بالطبع ينطبق فقط على الجزء C # / .NET من سؤالك




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

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

يجب أن يكون جعل الطُرُق عامًا / استخدام الحيل الملموسة مثل وضع كود للعلامة مرئيًا لاختبار التجميع دائمًا هو الحل الأخير.

فمثلا:

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

ريفاكتور هذا عن طريق إدخال كائن المدقق.

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

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




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

  1. اختبار الصندوق الاسود

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

  2. اختبار مربع أبيض.

    تعتقد مدرسة المربع الأبيض أنه من الطبيعي أن تستخدم المعرفة حول تنفيذ الفصل الدراسي ثم تختبر الفصل الدراسي لمعرفة أنها تعمل كما ينبغي.

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




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

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




لماذا لا تقسم خوارزمية إدارة المكدس إلى فئة الأداة المساعدة؟ يمكن لفئة المرافق إدارة المداخن وتوفير accessors العامة. يمكن أن تركز اختبارات الوحدة على تفاصيل التنفيذ. اختبارات عميقة للفصول الخوارزمية صعبة للغاية مفيدة في تجعد حالات حافة وضمان التغطية.

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




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




استخدم الانعكاس للوصول إلى المتغيرات الخاصة إذا كنت بحاجة إلى ذلك.

لكن في الحقيقة ، أنت لا تهتم بالحالة الداخلية للصف ، فأنت تريد فقط اختبار أن الطرق العامة تعيد ما تتوقعه في المواقف التي يمكنك توقعها.




تشير الكثير من الإجابات إلى اختبار الواجهة العامة فقط ، ولكن IMHO هذا غير واقعي - إذا كانت إحدى الطرق تتطلب 5 خطوات ، فستحتاج إلى اختبار هذه الخطوات الخمس بشكل منفصل ، وليس كل ذلك معًا. يتطلب ذلك اختبار جميع الطرق الخمس ، والتي قد تكون private (بخلاف الاختبار) .

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

نعم ، سيؤدي ذلك إلى ازدواج الملف والطبقة.

نعم ، هذا يجعل محددات public private زائدة عن الحاجة.

نعم ، هذا ألم في المؤخرة.

هذه ، للأسف ، واحدة من التضحيات الكثيرة التي نجريها لجعل الشفرة قابلة للاختبار . ربما تكون هناك لغة مستقبلية (أو حتى نسخة مستقبلية من C # / Java) تحتوي على ميزات تجعل اختبار قابلية الفصل والوحدة أكثر ملاءمة ؛ لكن في هذه الأثناء ، يجب علينا القفز من خلال هذه الأطواق.

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




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

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




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

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

متى:

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

يبدو أن الدين يتفوق على الهندسة.




Links