unit-testing - الرابع - test driven development شرح




كيف يمكنني اختبار وظيفة خاصة أو فصل يحتوي على طرق خاصة أو حقول أو دروس داخلية؟ (20)

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

  1. الطريقة الخاصة هي رمز ميت
  2. هناك رائحة تصميم بالقرب من الفصل الذي تختبره
  3. يجب ألا تكون الطريقة التي تحاول اختبارها خاصة

كيف يمكنني اختبار الوحدة (باستخدام xUnit) فئة تحتوي على أساليب خاصة داخلية أو حقول أو فئات متداخلة؟ أو وظيفة يتم جعلها خاصة بواسطة وجود ارتباط داخلي ( static في C / C ++) أو في مساحة اسم خاصة ( anonymous

يبدو من السوء تغيير معدل الوصول لطريقة أو وظيفة فقط لتكون قادرة على تشغيل الاختبار.


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


إذا كان لديك أحد تطبيقات Java القديمة ، ولا يُسمح لك بتغيير مستوى رؤية أساليبك ، فإن أفضل طريقة لاختبار الطرق الخاصة هي استخدام reflection .

داخليًا ، نستخدم المساعدين للحصول على / تعيين المتغيرات private static بالإضافة إلى استدعاء الأساليب private static . ستسمح لك الأنماط التالية بعمل أي شيء متعلق بالطرق والحقول الخاصة. بالطبع لا يمكنك تغيير المتغيرات private static final خلال التفكير.

Method method = targetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

والحقول:

Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

ملاحظات:
1. targetClass.getDeclaredMethod(methodName, argClasses) يتيح لك النظر في الطرق private . ينطبق الشيء نفسه على getDeclaredField .
2. setAccessible(true) للعب مع القراصنة.


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

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

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


إذا كنت تستخدم JUnit ، فقم بإلقاء نظرة على أدوات junit-addons . لديه القدرة على تجاهل نموذج أمان Java والوصول إلى الطرق والسمات الخاصة.


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

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);

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


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

هناك ميزة محتملة في اختبار الطرق الخاصة للفئة ، لا سيما مع التطوير القائم على الاختبار ، حيث ترغب في تصميم اختبارات صغيرة قبل كتابة أي رمز.

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

مزايا:

  • يمكن اختبار إلى دقة أدق

سلبيات:

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

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

في ما يلي مثال معقد حول كيفية عمل ذلك:

// Import statements and package declarations

public class ClassToTest
{
    private int decrement(int toDecrement) {
        toDecrement--;
        return toDecrement;
    }

    // Constructor and the rest of the class

    public static class StaticInnerTest extends TestCase
    {
        public StaticInnerTest(){
            super();
        }

        public void testDecrement(){
            int number = 10;
            ClassToTest toTest= new ClassToTest();
            int decremented = toTest.decrement(number);
            assertEquals(9, decremented);
        }

        public static void main(String[] args) {
            junit.textui.TestRunner.run(StaticInnerTest.class);
        }
    }
}

سيتم تصنيف الفئة الداخلية إلى ClassToTest$StaticInnerTest .

انظر أيضا: جافا نصيحة 106: الطبقات الداخلية الثابتة للمتعة والربح


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

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

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

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


كما قال الآخرون ... لا تختبر الطرق الخاصة مباشرة. وفيما يلي بعض الأفكار:

  1. الحفاظ على جميع الطرق صغيرة ومركزة (سهلة الاختبار ، وسهلة للعثور على ما هو خاطئ)
  2. استخدام أدوات تغطية التعليمات البرمجية. أنا أحب Cobertura (يا يوم سعيد ، يبدو وكأنه نسخة جديدة خارج!)

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


لاختبار الشفرة القديمة مع الفصول الدراسية الكبيرة والطارئة ، يكون من المفيد للغاية في الغالب اختبار الطريقة الخاصة (أو العامة) التي أكتبها الآن .

أنا استخدم junitx.util.PrivateAccessor- packageage لـ Java. الكثير من المنافع المفيدة للوصول إلى الطرق الخاصة والمجالات الخاصة.

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

لقد استخدمت reflection للقيام بذلك لجافا في الماضي ، وفي رأيي كان خطأ كبيرا.

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

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


من هذه المقالة: اختبار أساليب خاصة مع JUnit و SuiteRunner (Bill Venners) ، لديك في الأساس 4 خيارات:

  • لا تختبر الطرق الخاصة.
  • إعطاء طرق الوصول إلى الحزمة.
  • استخدم فئة اختبار متداخلة.
  • استخدم الانعكاس.

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

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

على سبيل المثال ، إذا كانت الطريقة التي سيتم اختبارها في src/main/java/mypackage/MyClass.java فيجب أن يتم وضع src/test/java/mypackage/MyClassTest.java في src/test/java/mypackage/MyClassTest.java . بهذه الطريقة ، يمكنك الوصول إلى طريقة الاختبار في صف الاختبار.


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

لذلك لا تختبر الطرق الخاصة.


يمكنك في إطار الربيع اختبار الطرق الخاصة باستخدام هذه الطريقة:

ReflectionTestUtils.invokeMethod()

فمثلا:

ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");

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

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


يرجى الاطلاع أدناه للحصول على سبيل المثال.

يجب إضافة عبارة الاستيراد التالية:

Whitebox.invokeMethod(obj, "privateMethod", "param1");

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

@UiThreadTest
public void testCompute() {

    // Given
    boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());

    // When
    boundBoxOfMainActivity.boundBox_getButtonMain().performClick();

    // Then
    assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}

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

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

import org.powermock.reflect.Whitebox;

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

وهو مثالي لاختبار بعض التعليمات البرمجية القديمة. استخدامه بعناية. ؛)

BoundBox


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

لا يمكنك استخدام الانعكاس للحصول على طرق خاصة من خارج فئة المالك ، حيث يؤثر معدل التعديل الخاص في التفكير أيضًا

هذا ليس صحيحا. يمكنك بكل تأكيد ، كما ورد في إجابة Cem Catikkas .





tdd