استخدام اختبار الضعف في Android

عند تصميم استراتيجية الاختبار لعنصر أو نظام، تكون هناك ثلاثة جوانب ذات صلة بالاختبار:

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

اطّلِع على ما يجب اختباره لمعرفة كيفية البدء في تحديد استراتيجية الاختبار.

العزلة والتبعيات

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

ومع ذلك، قد يعتمد الموضوع الذي يخضع للاختبار على أشخاص آخرين كي يعمل بشكل سليم. على سبيل المثال، قد يعتمد ViewModel على مستودع البيانات للعمل.

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

أنواع مضاعفات الاختبار

هناك أنواع مختلفة من اختبار الازدواج:

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

مثال: قاعدة بيانات في الذاكرة.

ولا تتطلب المواد المزيفة إطار عمل للمحاكاة، كما أنها خفيفة الوزن. هي مفضَّلة.

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

مثال: تحقق من أن إحدى الطرق في قاعدة البيانات تم استدعاءها مرة واحدة بالضبط.

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

مثال: دالة فارغة تم تمريرها كاستدعاء نقرة.

الجاسوسية برنامج تضمين فوق كائن حقيقي يتتبع أيضًا بعض المعلومات الإضافية، على غرار النماذج التجريبية. وعادة ما يتم تجنبها لزيادة التعقيد. وبالتالي يفضَّل تقديم الصور المزيفة أو الوهمية عن الجواسيس.
التظليل يتم استخدام صورة مزيّفة في Robolectric.

مثال على استخدام ملف شخصي وهمي

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

object FakeUserRepository : UserRepository {
    fun getUsers() = listOf(UserAlice, UserBob)
}

val const UserAlice = User("Alice")
val const UserBob = User("Bob")

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

يمكن أن تعرض التبعية الزائفة بيانات معروفة بدون الاعتماد على مصادر البيانات البعيدة
الشكل 1: تبعية وهمية في اختبار الوحدة.

يتأكد الاختبار التالي من أن ViewModel يعرض اسم المستخدم الأول بشكلٍ صحيح للعرض.

@Test
fun viewModelA_loadsUsers_showsFirstUser() {
    // Given a VM using fake data
    val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init

    // Verify that the exposed data is correct
    assertEquals(viewModel.firstUserName, UserAlice.name)
}

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

استبدال المكوّنات وإدخال التبعية

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

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

الشكل 2: اختبار كبير يشمل معظم التطبيق والبيانات المزيفة عن بُعد.

يمكنك تصميم تطبيقك لتحقيق هذه المرونة يدويًا، ولكننا ننصح باستخدام إطار عمل إدراج التبعية مثل Hilt لاستبدال المكونات في تطبيقك في وقت الاختبار. اطّلِع على دليل اختبار Hilt.

روبوت

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

تُعدّ لعبة Robolectric أداة محاكاة، لذا يجب ألا تحل محل اختبارات الوحدات البسيطة أو يمكن استخدامها لإجراء اختبارات التوافق. إنه يوفر السرعة ويقلل التكلفة على حساب الدقة المنخفضة في بعض الحالات. ومن الأساليب الجيدة لاختبارات واجهة المستخدم جعلها متوافقة مع كل من اختبارات Robolectric والاختبارات المُجدوَلة وتحديد وقت تشغيلها بناءً على الحاجة إلى اختبار الوظائف أو التوافق. يمكن إجراء اختبارات Espresso وCompose على Robolectric.