يُعد هذا الدرس التطبيقي جزءًا من الدورة التدريبية المتقدّمة لنظام التشغيل Android في لغة Kotlin. ستحصل على أقصى استفادة من هذه الدورة التدريبية إذا كنت تعمل من خلال الدروس التطبيقية حول الترميز بالتسلسل، ولكن هذا ليس إلزاميًا. يتم إدراج جميع الدروس التطبيقية حول ترميز الدورات التدريبية في الصفحة المقصودة لبرنامج Android المتقدّم في لغة ترميز Kotlin.
مقدمة
عند تنفيذ الميزة الأولى لتطبيقك الأول، من المحتمل أنك شغّلت الرمز للتأكد من أنها عملت على النحو المتوقع. أجريت اختبارًا، وإن كان اختبارًا يدويًا. أثناء استمرارك في إضافة ميزات وتحديثها، من المحتمل أنّك واصلت أيضًا تشغيل الرمز والتأكّد من أنه يعمل. ولكن تنفيذ ذلك يدويًا في كل مرة يكون فيه متعبًا وعرضةً للأخطاء ولا يمكن توسيعه.
وتُعد أجهزة الكمبيوتر وسيلة رائعة لتوسيع نطاقها والتشغيل التلقائي. لذا يكتب مطوّرو البرامج في الشركات الكبيرة والصغيرة الاختبارات المبرمجة، وهي اختبارات يتم تشغيلها بواسطة البرنامج ولا تتطلب منك تشغيل التطبيق يدويًا للتحقّق من عمل الرمز.
ما ستتعرّف عليه في هذه السلسلة من الدروس التطبيقية حول الترميز هو كيفية إنشاء مجموعة من الاختبارات (تُعرف باسم مجموعة أدوات الاختبار لتطبيق حقيقي.
يتناول هذا الدرس التطبيقي حول الترميز أساسيات الاختبار على نظام التشغيل Android، وستكتب اختباراتك الأولى وستتعلّم كيفية اختبار LiveData
وViewModel
.
ما يجب معرفته
ويجب أن تكون على دراية بما يلي:
- لغة البرمجة Kotlin
- مكتبات Jetpack الأساسية التالية:
ViewModel
وLiveData
- بنية التطبيق، باتباع النمط من الدليل إلى بنية التطبيق والدروس التطبيقية حول ترميز أساسيات Android
ما ستتعرَّف عليه
ستتعرّف على المواضيع التالية:
- كيفية كتابة اختبارات الوحدة وتنفيذها على Android
- كيفية استخدام التطوير المستند إلى الاختبار
- كيفية اختيار الاختبارات الآلية والاختبارات المحلية
وستتعرّف على معلومات حول المكتبات ومفاهيم الرموز التالية:
الإجراءات التي ستنفذّها
- إعداد الاختبارات المحلية والمبرمَجة وتنفيذها وتفسيرها في نظام التشغيل Android
- كتابة اختبارات الوحدات في Android باستخدام JUnit4 وHemcrest.
- كتابة اختبارات
LiveData
وViewModel
البسيطة
في هذه السلسلة من الدروس التطبيقية حول الترميز، ستعمل مع تطبيق الملاحظات في قائمة المهام. ويتيح لك التطبيق تدوين المهام لإكمالها وعرضها في قائمة. ويمكنك بعد ذلك وضع علامة "مكتملة" أو "غير مكتملة" أو فلترتها أو حذفها.
هذا التطبيق مكتوب بلغة Kotlin ويحتوي على عدة شاشات ويستخدم مكونات Jetpack ويتّبع البنية من دليل إلى بنية التطبيق. من خلال تعلّم كيفية اختبار هذا التطبيق، ستتمكّن من اختبار التطبيقات التي تستخدم المكتبات والبنية نفسها.
للبدء، يُرجى تنزيل الرمز:
بدلاً من ذلك، يمكنك إنشاء نسخة طبق الأصل من مستودع Github للرمز:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout starter_code
في هذه المهمة، ستتمكن من تشغيل التطبيق واستكشاف قاعدة الرموز.
الخطوة 1: تشغيل نموذج التطبيق
بعد تنزيل تطبيق المهام المطلوبة، افتحه في Android Studio وشغِّله. يجب أن يتم تجميعها. استكشِف التطبيق من خلال إجراء ما يلي:
- يمكنك إنشاء مهمة جديدة باستخدام الزر عائم للإجراء. أدخِل عنوانًا أولاً، ثم أدخِل معلومات إضافية عن المهمة. احفظه باستخدام علامة الاستفهام FAB التي تشير إلى اللون الأخضر.
- في قائمة المهام، انقر على عنوان المهمة التي أكملتها وألقِ نظرة على شاشة تفاصيل تلك المهمة للاطّلاع على بقية الوصف.
- في القائمة أو على شاشة التفاصيل، ضع علامة في مربّع اختيار هذه المهمة لضبط حالتها على مكتملة.
- ارجع إلى شاشة المهام، وافتح قائمة الفلاتر، وفلتِر المهام حسب الحالة نشط ومكتمل.
- افتح لائحة التنقل وانقر على الإحصاءات.
- ارجع إلى شاشة النظرة العامة، ومن قائمة درج التنقّل، اختَر محو المهام المكتملة لحذف جميع المهام بالحالة مكتملة.
الخطوة 2: استكشاف نموذج رمز التطبيق
يستند تطبيق قائمة المهام إلى نموذج الهندسة المعمارية الزرقاء الشائعة واختباره (باستخدام إصدار البنية التفاعلية من النموذج). يتّبع التطبيق البنية من دليل إلى بنية التطبيق. وهي تستخدم ViewViews مع أجزاء ومستودع وغرفة. إذا كنت على دراية بأي من الأمثلة التالية، يكون لهذا التطبيق بنية مشابهة:
- غرفة بدرس تطبيقي حول الترميز
- الدروس التطبيقية حول ترميز أساسيات Android Kotlin
- الدروس التطبيقية التدريبية المتقدّمة حول ترميز Android
- نموذج دوار الشمس لنظام التشغيل Android
- تطوير تطبيقات متوافقة مع Android باستخدام دورة تدريبية على لغة Kotlin Udacity
من المهم فهم البنية العامة للتطبيق بدلاً من فهم عميق للمنطق في أي طبقة.
في ما يلي ملخص الحِزم التي ستظهر لك:
الحزمة: | |
| إضافة شاشة مهمة أو تعديلها: رمز طبقة واجهة المستخدم لإضافة مهمة أو تعديلها. |
| طبقة البيانات: تتعامل هذه الطبقة مع طبقة بيانات المهام. ويحتوي هذا المورد على قاعدة البيانات والشبكة ورمز المستودع. |
| شاشة الإحصاءات: رمز طبقة واجهة المستخدِم لشاشة الإحصاءات. |
| شاشة تفاصيل المهمة: رمز طبقة واجهة المستخدم لمَهمة واحدة. |
| شاشة المهام: رمز طبقة واجهة المستخدم لقائمة جميع المهام. |
| فئات الأدوات المساعدة: الصفوف المشتركة المستخدمة في أجزاء مختلفة من التطبيق، مثلاً لتنسيق التمرير السريع الذي يتم استخدامه في شاشات متعددة. |
طبقة البيانات (data.)
يتضمن هذا التطبيق طبقة محاكاة للشبكة، في حزمة بعيد، وطبقة قاعدة بيانات، في الحزمة المحلية. لتبسيط الأمر، تمت في هذا المشروع محاكاة طبقة الشبكة باستخدام HashMap
فقط مع تأخير، بدلاً من تقديم طلبات شبكة حقيقية.
إحداثيات DefaultTasksRepository
أو وسيطات بين طبقة الاتصال وطبقة قاعدة البيانات وهي ما يعرض البيانات إلى طبقة واجهة المستخدم.
طبقة واجهة المستخدم ( .addedittask, .statistics, .taskdetail, .tasks)
وتحتوي كل حزمة من حزم طبقة واجهة المستخدم على جزء ونموذج عرض، إلى جانب أي فئات أخرى مطلوبة لواجهة المستخدم (مثل محوِّل لقائمة المهام). TaskActivity
هي النشاط الذي يتضمّن جميع الأجزاء.
التنقل
يتم التحكم في التنقل داخل التطبيق من خلال مكوّن التنقل. يتم تحديد ذلك في ملف nav_graph.xml
. يتم تشغيل التنقّل في نماذج الملف الشخصي باستخدام فئة Event
، كما تحدّد نماذج العرض الوسيطات التي يجب تمريرها. وتلاحظ الأجزاء Event
وهي تنفّذ عمليات التنقل الفعلية بين الشاشات.
في هذه المهمة، ستُجري اختباراتك الأولى.
- في "استوديو Android"، افتح جزء المشروع واعثر على هذه المجلدات الثلاثة:
com.example.android.architecture.blueprints.todoapp
com.example.android.architecture.blueprints.todoapp (androidTest)
com.example.android.architecture.blueprints.todoapp (test)
تُعرف هذه المجلدات باسم مجموعات المصادر. مجموعات المصادر هي مجلدات تحتوي على رمز المصدر لتطبيقك. تحتوي مجموعات المصادر على اللون الأخضر (androidTest وtest) لاختباراتك. عند إنشاء مشروع جديد على Android، ستحصل تلقائيًا على ثلاث مجموعات مصادر. وهي:
main
: يحتوي على رمز تطبيقك. تتم مشاركة هذا الرمز بين جميع إصدارات التطبيق المختلفة التي يمكنك إنشاؤها (تُعرف باسم صيغ الإصدار)androidTest
: يحتوي على اختبارات تُعرف باسم الاختبارات التي تم قياسها.test
: يحتوي على اختبارات تُعرف باسم الاختبارات المحلية.
الفرق بين الاختبارات المحلية والاختبارات الآلية في طريقة تنفيذها.
الاختبارات المحلية (test
مجموعة المصادر)
ويتم إجراء هذه الاختبارات محليًا على جهاز JVM الخاص بك على جهاز التطوير ولا تتطلب استخدام جهاز محاكاة أو جهاز فعلي. ولهذا السبب، تعمل هذه الرموز بسرعة، لكن الدقّة المنخفضة فيها، ما يعني أنّها تعمل على النحو الأدنى في العالم الحقيقي.
يتم تمثيل الاختبارات المحلية في "استوديو Android" برمز مثلث أخضر وأحمر.
الاختبارات الآلية (androidTest
مجموعة المصدر)
ويتم تنفيذ هذه الاختبارات على أجهزة Android الحقيقية أو التي يتم محاكاتها، لذا تعكس ما سيحدث في العالم الحقيقي، ولكنها أبطأ أيضًا.
ويُستخدَم نظام التشغيل Android في اختبارات Android التي تم قياسها، بالإضافة إلى رمز مثلث أخضر وأحمر.
الخطوة الأولى: إجراء اختبار محلي
- افتح المجلد
test
حتى تعثر على الملف ExampleUnitTest.kt. - انقر بزر الماوس الأيمن عليها واختر تشغيل ExampleUnitTest.
من المفترض أن يظهر لك الناتج التالي في نافذة Run (تشغيل) في أسفل الشاشة:
- ضَع علامة في مربّعات الاختيار الخضراء ووسِّع نتائج الاختبار للتأكّد من اجتياز اختبار واحد يُسمّى
addition_isCorrect
. من المفيد أن تعرف أن الإضافة تعمل كما هو متوقع.
الخطوة الثانية: تعذّر إجراء الاختبار
في ما يلي الاختبار الذي أجرته للتو.
ExampleUnitTest.kt
// A test class is just a normal class
class ExampleUnitTest {
// Each test is annotated with @Test (this is a Junit annotation)
@Test
fun addition_isCorrect() {
// Here you are checking that 4 is the same as 2+2
assertEquals(4, 2 + 2)
}
}
ملاحظة: الاختبارات
- تمثّل فئة في إحدى مجموعات مصادر الاختبار.
- تحتوي على الدوال التي تبدأ بالتعليق التوضيحي
@Test
(كل وظيفة عبارة عن اختبار واحد). - تحتوي على عبارات تأكيد.
يستخدِم نظام التشغيل Android مكتبة الاختبار JUnit للاختبار (في هذا الدرس التطبيقي حول الترميز JUnit4). ومصدر كل من التأكيدات والتعليقات التوضيحية في @Test
هو JUnit.
يمثّل التأكيد أساس الاختبار. عبارة عن بيان رموز للتحقق من عمل الرمز أو التطبيق على النحو المتوقّع. في هذه الحالة، تكون عملية التأكيد assertEquals(4, 2 + 2)
التي تتحقق من أن القيمة تساوي 2 + 2.
وللاطّلاع على نتيجة الاختبار الذي تعذّر اجتيازه، يُرجى إضافة تأكيد بأنه يمكنك رؤيته بسهولة. وسيتحقق ذلك من أن الرقم 3 يساوي 1+1.
- إضافة
assertEquals(3, 1 + 1)
إلى اختبارaddition_isCorrect
.
ExampleUnitTest.kt
class ExampleUnitTest {
// Each test is annotated with @Test (this is a Junit annotation)
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
assertEquals(3, 1 + 1) // This should fail
}
}
- إجراء الاختبار.
- في نتائج الاختبار، لاحظ علامة X بجانب الاختبار.
- يُرجى أيضًا ملاحظة ما يلي:
- تعذّر إدخال تأكيد واحد في الاختبار كلّه.
- يتم إخبارك بالقيمة المتوقعة (3) مقابل القيمة التي تم حسابها فعليًا (2).
- ويتم توجيهك إلى سطر التأكيد
(ExampleUnitTest.kt:16)
الذي تعذّر تنفيذه.
الخطوة 3: إجراء اختبار قياس الأداء
تم العثور على الاختبارات المُستخدَمة في مجموعة المصدر androidTest
.
- افتح مجموعة مصادر
androidTest
. - شغِّل الاختبار باسم
ExampleInstrumentedTest
.
ExampleMachineedTest
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.android.architecture.blueprints.reactive",
appContext.packageName)
}
}
وبخلاف الاختبار المحلي، يتم تشغيل هذا الاختبار على أحد الأجهزة (في المثال أدناه هاتف Pixel 2 المُحاكَى):
إذا كان لديك جهاز متصل أو المحاكي قيد التشغيل، من المفترض أن يظهر لك الاختبار قيد التشغيل على المحاكي.
في هذه المهمة، ستكتب اختبارات للنطاق getActiveAndCompleteStats
، الذي يحسب النسبة المئوية لإحصاءات المهام النشطة والكاملة لتطبيقك. يمكنك الاطّلاع على هذه الأرقام على شاشة الإحصاءات في التطبيق.
الخطوة الأولى: إنشاء صف اختبار
- في مجموعة المصدر
main
، فيtodoapp.statistics
، افتحStatisticsUtils.kt
. - ابحث عن الدالة
getActiveAndCompletedStats
.
AnalyticsUtils.kt
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
val totalTasks = tasks!!.size
val numberOfActiveTasks = tasks.count { it.isActive }
val activePercent = 100 * numberOfActiveTasks / totalTasks
val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks
return StatsResult(
activeTasksPercent = activePercent.toFloat(),
completedTasksPercent = completePercent.toFloat()
)
}
data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float)
تقبل الدالة getActiveAndCompletedStats
قائمة بالمهام وتعرض StatsResult
. StatsResult
هي فئة بيانات تحتوي على رقمين، والنسبة المئوية للمهام التي تم إكمالها، والنسبة المئوية النشطة.
ويمنحك "استوديو Android" أدوات لإنشاء الرموز البديلة للمساعدة في تنفيذ الاختبارات على هذه الوظيفة.
- انقر بزر الماوس الأيمن على
getActiveAndCompletedStats
، ثم اختَر إنشاء >؛ اختبار.
يظهر مربع الحوار إنشاء اختبار:
- غيِّر اسم الصف: إلى
StatisticsUtilsTest
(بدلاً منStatisticsUtilsKtTest
؛ من الأفضل قليلاً عدم استخدام KT في اسم الفئة التجريبية). - الاحتفاظ بالإعدادات التلقائية الأخرى. الوحدة 4 هي مكتبة الاختبارات المناسبة. حزمة الوجهة صحيحة (تعكس موقع الصف
StatisticsUtils
) ولا تحتاج إلى وضع علامة في أيٍّ من مربّعات الاختيار (سيؤدي هذا فقط إلى إنشاء رمز إضافي، ولكنك ستكتب الاختبار من البداية). - اضغط على حسنًا.
يظهر مربع الحوار اختيار دليل الوجهة:
عليك إجراء اختبار محلي لأن وظيفتك تُجري عمليات حسابية ولن تتضمن أي رموز خاصة بنظام Android. لذلك، لا داعي لتشغيله على جهاز حقيقي أو محاكٍ.
- اختَر الدليل
test
(وليسandroidTest
) لأنك ستكتب الاختبارات المحلية. - انقر على حسنًا.
- لاحِظ الصف الدراسي
StatisticsUtilsTest
فيtest/statistics/
.
الخطوة الثانية: كتابة دالة الاختبار الأولى
ستُجري اختبارًا للتحقق من:
- إذا لم تكن هناك مهام مكتملة ومهمة نشطة واحدة،
- أنّ النسبة المئوية للاختبارات النشطة هي %100
- وتكون نسبة المهام المكتملة 0%.
- فتح
StatisticsUtilsTest
- أنشئ دالة باسم
getActiveAndCompletedStats_noCompleted_returnsHundredZero
.
AnalyticsUtilsTest.kt
class StatisticsUtilsTest {
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task
// Call your function
// Check the result
}
}
- أضف التعليق التوضيحي
@Test
فوق اسم الدالة للإشارة إلى اختباره. - إنشاء قائمة بالمهام.
// Create an active task
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
- يمكنك الاتصال بـ
getActiveAndCompletedStats
لتنفيذ هذه المهام.
// Call your function
val result = getActiveAndCompletedStats(tasks)
- يمكنك التحقق من
result
كما توقعت، باستخدام التأكيدات.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
إليك الرمز الكامل.
AnalyticsUtilsTest.kt
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task (the false makes this active)
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
// Call your function
val result = getActiveAndCompletedStats(tasks)
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
}
}
- نفِّذ الاختبار (انقر بزر الماوس الأيمن على
StatisticsUtilsTest
واختَر تشغيل).
يجب أن يتم اجتيازه:
الخطوة 3: إضافة تبعية الهمكرست
ونظرًا لأن اختباراتك تعمل كوثائق لما ينفذه الرمز، فإنها تكون رائعة عندما يكون من السهل على الإنسان قراءتها. قارِن التأكيدَين التاليَين:
assertEquals(result.completedTasksPercent, 0f)
// versus
assertThat(result.completedTasksPercent, `is`(0f))
ويكون التأكيد الثاني كما لو كان جملة بشرية. وهو مكتوب باستخدام إطار عمل تأكيد يُسمى Hamcrest. تُعد مكتبة الحقائق أداة أخرى جيدة لكتابة التأكيدات القابلة للقراءة. ستستخدم هامكرست في هذا الدرس التطبيقي لكتابة التأكيدات.
- افتح
build.grade (Module: app)
وأضِف الاعتمادية التالية.
app/build.gradle
dependencies {
// Other dependencies
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}
عادةً، يتم استخدام implementation
عند إضافة تبعية، ولكن أنت الآن تستخدم testImplementation
. عندما تكون مستعدًا لمشاركة تطبيقك مع العالم، من الأفضل عدم تضخم حجم حزمة APK مع أي رمز اختباري أو تبعيات في تطبيقك. ويمكنك تحديد ما إذا كان يجب تضمين مكتبة في الرمز الرئيسي أو التجريبي باستخدام عمليات ضبط الدرجات. وتتضمن عمليات الضبط الأكثر شيوعًا ما يلي:
implementation
: تتوفَّر الاعتمادية في جميع مجموعات المصادر، بما في ذلك مجموعات مصادر الاختبار.testImplementation
: لا تتوفر الاعتمادية إلا في مجموعة مصادر الاختبار.androidTestImplementation
: لا تتوفر التبعية إلا في مجموعة المصدرandroidTest
.
يحدّد الضبط الذي تستخدمه، أي أماكن استخدام الاعتمادية. إذا كتبت:
testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
ويعني هذا أن حملة "هامكرست" لن تتوفر إلا في مجموعة مصادر الاختبار. وتضمن أيضًا عدم تضمين تطبيق Hacrest في تطبيقك النهائي.
الخطوة 4: استخدام Hacrest لكتابة تأكيدات
- عدِّل اختبار
getActiveAndCompletedStats_noCompleted_returnsHundredZero()
لاستخدامassertThat
من Humestest بدلاً منassertEquals
.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
لاحظ أنه يمكنك استخدام الاستيراد import org.hamcrest.Matchers.`is`
إذا طُلب منك ذلك.
سيبدو الاختبار النهائي على النحو الموضّح أدناه الرمز.
AnalyticsUtilsTest.kt
import com.example.android.architecture.blueprints.todoapp.data.Task
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
// Create an active tasks (the false makes this active)
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
// Call your function
val result = getActiveAndCompletedStats(tasks)
// Check the result
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
}
}
- شغِّل الاختبار المعدَّل للتأكّد من أنه لا يزال مفعَّلاً.
لن يُعلّمك هذا الدرس التطبيقي كلّ التفاصيل حول مدينة هامكرست، لذا إذا أردت معرفة المزيد من المعلومات، يمكنك الاطّلاع على البرنامج التعليمي الرسمي.
هذه مهمة اختيارية للممارسة.
في هذه المهمة، ستكتب المزيد من الاختبارات باستخدام JUnit وهامكرست. وستكتب أيضًا الاختبارات باستخدام استراتيجية مستمدة من ممارسة البرنامج في التطوير المستند إلى الاختبار. التطوير المستند إلى الاختبار أو TDD هو مدرسة برمجة للأفكار تنص على كتابة اختباراتك أولاً بدلاً من كتابة رمز الميزة أولاً. وبعد ذلك، اكتب رمز الميزة بهدف اجتياز اختباراتك.
الخطوة الأولى: كتابة الاختبارات
كتابة الاختبارات عندما تكون لديك قائمة مهام عادية:
- إذا كانت هناك مهمة واحدة مكتملة ولم تكن هناك أي مهام نشطة، يجب أن تكون النسبة المئوية
activeTasks
هي0f
، ويجب أن تكون النسبة المئوية للمهام المكتملة100f
. - إذا كانت هناك مهمتان مكتملتان وثلاث مهام نشطة، يجب أن تكون النسبة المئوية المكتملة
40f
والنسبة المئوية النشطة هي60f
.
الخطوة الثانية: كتابة اختبار للخطأ
هناك خطأ في رمز getActiveAndCompletedStats
كما هو مكتوب. لاحظ كيف لا تتعامل هذه الأداة بشكل صحيح مع ما يحدث إذا كانت القائمة فارغة أو فارغة. وفي كلتا الحالتين، يجب أن تكون النسب المئوية صفرًا.
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
val totalTasks = tasks!!.size
val numberOfActiveTasks = tasks.count { it.isActive }
val activePercent = 100 * numberOfActiveTasks / totalTasks
val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks
return StatsResult(
activeTasksPercent = activePercent.toFloat(),
completedTasksPercent = completePercent.toFloat()
)
}
لإصلاح الرمز وكتابة الاختبارات، يمكنك استخدام التطوير التجريبي. يتّبع التطوير المستند إلى الاختبار هذه الخطوات.
- اكتب الاختبار باستخدام البنية المحددة والوقت ثم المثال واسمًا يتبع الاصطلاح.
- تأكّد من تعذّر الاختبار.
- اكتب الحد الأدنى للرمز للحصول على الاختبار لاجتيازه.
- تكرار الخطوات السابقة مع جميع الاختبارات
بدلاً من بدء إصلاح الخطأ، ستبدأ بكتابة الاختبارات أولاً. ويمكنك بعد ذلك تأكيد أنّ لديك اختبارات تحميك من إعادة عرض هذه الأخطاء في المستقبل عن طريق الخطأ.
- إذا كانت هناك قائمة فارغة (
emptyList()
)، يجب أن تكون كلتا النسبتين صفرًا. - إذا حدث خطأ أثناء تحميل المهام، ستكون القائمة
null
، ويجب أن تكون النسب المئوية 0f. - إجراء اختباراتك وتأكيد تعذّر تنفيذها:
الخطوة الثالثة. إصلاح الخطأ
الآن بعد أن أنشأت اختباراتك، أصلح الخطأ.
- أصلح الخطأ في
getActiveAndCompletedStats
عن طريق عرض0f
إذا كانtasks
هوnull
أو فارغ:
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
return if (tasks == null || tasks.isEmpty()) {
StatsResult(0f, 0f)
} else {
val totalTasks = tasks.size
val numberOfActiveTasks = tasks.count { it.isActive }
StatsResult(
activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
)
}
}
- عليك إجراء اختباراتك مرة أخرى والتأكّد من اجتياز جميع الاختبارات الآن.
من خلال اتباع اختبار TDD وكتابة الاختبارات أولاً، ساعدت في ضمان:
- دائمًا ما ترتبط الوظائف الجديدة باختبارات مرتبطة؛ لذا تعمل اختباراتك كمستندات لما يفعله الرمز.
- تعمل اختباراتك على التحقق من النتائج الصحيحة والحماية من الأخطاء التي رأيتها من قبل.
الحل: كتابة المزيد من الاختبارات
وفي ما يلي جميع الاختبارات ورمز الميزة المقابل.
AnalyticsUtilsTest.kt
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
val tasks = listOf(
Task("title", "desc", isCompleted = false)
)
// When the list of tasks is computed with an active task
val result = getActiveAndCompletedStats(tasks)
// Then the percentages are 100 and 0
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
}
@Test
fun getActiveAndCompletedStats_noActive_returnsZeroHundred() {
val tasks = listOf(
Task("title", "desc", isCompleted = true)
)
// When the list of tasks is computed with a completed task
val result = getActiveAndCompletedStats(tasks)
// Then the percentages are 0 and 100
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(100f))
}
@Test
fun getActiveAndCompletedStats_both_returnsFortySixty() {
// Given 3 completed tasks and 2 active tasks
val tasks = listOf(
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = true),
Task("title", "desc", isCompleted = false),
Task("title", "desc", isCompleted = false)
)
// When the list of tasks is computed
val result = getActiveAndCompletedStats(tasks)
// Then the result is 40-60
assertThat(result.activeTasksPercent, `is`(40f))
assertThat(result.completedTasksPercent, `is`(60f))
}
@Test
fun getActiveAndCompletedStats_error_returnsZeros() {
// When there's an error loading stats
val result = getActiveAndCompletedStats(null)
// Both active and completed tasks are 0
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(0f))
}
@Test
fun getActiveAndCompletedStats_empty_returnsZeros() {
// When there are no tasks
val result = getActiveAndCompletedStats(emptyList())
// Both active and completed tasks are 0
assertThat(result.activeTasksPercent, `is`(0f))
assertThat(result.completedTasksPercent, `is`(0f))
}
}
AnalyticsUtils.kt
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {
return if (tasks == null || tasks.isEmpty()) {
StatsResult(0f, 0f)
} else {
val totalTasks = tasks.size
val numberOfActiveTasks = tasks.count { it.isActive }
StatsResult(
activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
)
}
}
أحسنت في أساسيات كتابة الاختبارات وتنفيذها. بعد ذلك، ستتعرّف على كيفية كتابة اختبارات ViewModel
وLiveData
الأساسية.
في باقي الدرس التطبيقي حول الترميز، ستتعرّف على كيفية كتابة اختبارات لصفَي Android شائعتين في معظم التطبيقات، وهما ViewModel
وLiveData
.
تبدأ بكتابة اختبارات TasksViewModel
.
ستركّز على الاختبارات التي لها كل منطقها في نموذج العرض ولا تعتمد على رمز المستودع. يتضمّن رمز المستودع رمزًا غير متزامن وقواعد البيانات واستدعاءات الشبكة، ما يزيد من تعقيد الاختبار. وبذلك، ستتجنب هذا الأمر في الوقت الحالي وركِّز على كتابة اختبارات لوظيفة طريقة العرض التي لا تختبر أي شيء في المستودع مباشرةً.
سيتحقق الاختبار الذي ستكتبه من أنه عند استدعاء الطريقة addNewTask
، يتم تنشيط Event
لفتح نافذة المهمة الجديدة. رمز التطبيق الذي ستعمل على اختباره.
TasksViewmodel.kt
fun addNewTask() {
_newTaskEvent.value = Event(Unit)
}
الخطوة الأولى: إنشاء فئة ClassViewmodelTest
باتّباع الخطوات نفسها التي اتّبعتها في StatisticsUtilTest
، ستنشئ في هذه الخطوة ملف اختبار للتطبيق TasksViewModelTest
.
- افتح الصف الذي ترغب في اختباره في حزمة
tasks
،TasksViewModel.
- في الرمز، انقر بزر الماوس الأيمن على اسم الفئة
TasksViewModel
-> إنشاء -> اختبار.
- في شاشة إنشاء الاختبار، انقر على حسنًا للقبول (لا حاجة إلى تغيير أي من الإعدادات التلقائية).
- في مربع الحوار اختيار دليل الوجهة، اختَر الدليل test.
الخطوة الثانية: ابدأ كتابة TestView.
في هذه الخطوة، يمكنك إضافة اختبار نموذج المشاهدة لاختبار أنه عند استدعاء الطريقة addNewTask
، يتم تنشيط Event
لفتح نافذة المهمة الجديدة.
- أنشِئ اختبارًا جديدًا باسم
addNewTask_setsNewTaskEvent
.
TasksViewmodelTest.kt
class TasksViewModelTest {
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh TasksViewModel
// When adding a new task
// Then the new task event is triggered
}
}
ماذا عن سياق الطلبات؟
عندما تنشئ مثيلاً من TasksViewModel
للاختبار، تتطلب أداة الإنشاء سياق التطبيق. ولكن في هذا الاختبار، لا تنشئ تطبيقًا كاملاً فيه أنشطة وواجهة مستخدم وأجزاء، فكيف تحصل على سياق للتطبيق؟
TasksViewmodelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(???)
تتضمن مكتبات اختبارات AndroidX فئات وطرقًا توفر لك إصدارات من المكونات مثل التطبيقات والأنشطة المخصصة للاختبارات. عندما يكون لديك اختبار محلي حيث تريد محاكاة فئات إطار عمل Android (مثل سياق التطبيق)، اتبع هذه الخطوات لإعداد اختبار AndroidX بشكل صحيح:
- إضافة تبعيات AndroidX Test والاعتماديات الخارجية
- إضافة الاعتمادية لمكتبة Robolectric Test
- إضافة تعليقات توضيحية إلى الصف باستخدام مُجري اختبار AndroidJunit4
- كتابة رمز اختبار AndroidX
عليك إكمال هذه الخطوات ثم لفهم ما تفعله معًا.
الخطوة الثالثة. إضافة تبعيات الدرجات
- انسخ هذه الارتباطات في ملف تطبيقك على
build.gradle
وحدة التطبيق الخاص بك لإضافة تبعيات اختبار AndroidX الأساسية والتابعة، بالإضافة إلى اعتمادية اختبار Robolectric.
app/build.gradle
// AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"
testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"
testImplementation "org.robolectric:robolectric:$robolectricVersion"
الخطوة الرابعة: إضافة مشغّل اختبار JUnit
- أضِف
@RunWith(AndroidJUnit4::class)
أعلى صف الاختبار.
TasksViewmodelTest.kt
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Test code
}
الخطوة الخامسة. استخدام اختبار AndroidX
في هذه المرحلة، يمكنك استخدام مكتبة اختبار AndroidX. ويتضمن ذلك الطريقة ApplicationProvider.getApplicationContex
t
التي تحصل على سياق تطبيق.
- يمكنك إنشاء
TasksViewModel
باستخدامApplicationProvider.getApplicationContext()
من مكتبة اختبارات AndroidX.
TasksViewmodelTest.kt
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
- تواصل هاتفيًا مع "
addNewTask
" على الرقمtasksViewModel
.
TasksViewmodelTest.kt
tasksViewModel.addNewTask()
وفي هذه المرحلة، من المفترض أن يظهر الاختبار على النحو التالي:
TasksViewmodelTest.kt
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
// TODO test LiveData
}
- أجرِ اختبارك للتأكّد من توافقه.
المفهوم: كيف يعمل اختبار AndroidX؟
ما المقصود باختبار AndroidX؟
AndroidX Test هو مجموعة من المكتبات التي يمكن اختبارها. ويتضمّن صفوفًا وطرقًا تمنحك إصدارات من المكوّنات، مثل التطبيقات والأنشطة، وتكون مخصّصة للاختبارات. على سبيل المثال، هذا الرمز الذي كتبته هو مثال على وظيفة اختبار AndroidX للحصول على سياق التطبيق.
ApplicationProvider.getApplicationContext()
ومن فوائد واجهات برمجة تطبيقات Test Android أنّها مُصمَّمة للعمل على كلٍّ من الاختبارات المحلية والاختبارات التي يتم قياسها. وهذا أمر رائع للأسباب التالية:
- يمكنك إجراء الاختبار نفسه كاختبار محلي أو اختبار معدّ مسبقًا.
- ولن تحتاج إلى معرفة واجهات برمجة تطبيقات مختلفة للاختبارات المحلية في مقابل الاختبارات المحلية.
على سبيل المثال، لأنك كتبت الرمز باستخدام مكتبات اختبار AndroidX، يمكنك نقل صف TasksViewModelTest
من مجلد test
إلى مجلد androidTest
وسيستمر إجراء الاختبارات. يعمل getApplicationContext()
بشكل مختلف قليلاً اعتمادًا على ما إذا كان يتم تشغيله كاختبار محلي أو اختبار أداة:
- وإذا كان الاختبار قائمًا على اختبار في التطبيق، سيتم توفير سياق التطبيق الفعلي له عند تشغيله في محاكي أو عند الاتصال بجهاز حقيقي.
- إذا كان الاختبار محليًا، يستخدم بيئة محاكاة لنظام التشغيل Android.
ما هي Robolectric؟
توفّر Robolectric بيئة محاكاة Android المتوافقة مع اختبار AndroidX للاختبارات المحلية. Robolectric هي مكتبة تنشئ بيئة Android افتراضية للاختبارات وتعمل بشكل أسرع من تشغيل المحاكي أو التشغيل على أحد الأجهزة. بدون الاعتمادية Robolectric، سيظهر لك الخطأ التالي:
ما هي وظيفة @RunWith(AndroidJUnit4::class)
؟
أداة تنفيذ الاختبار هي أحد مكوّنات الوحدة J التي يتم إجراء الاختبارات. بدون إجراء اختبار، لن يتم إجراء الاختبارات. هناك جهاز تشغيل تلقائي للاختبار بواسطة JUnit والذي تحصل عليه تلقائيًا. يستبدل @RunWith
مُجري الاختبار التلقائي هذا.
يتيح مُجري اختبار AndroidJUnit4
لاختبار AndroidX إجراء اختبارك بشكل مختلف اعتمادًا على ما إذا كان يتم إجراؤه على اختبارات محلية أو لا.
الخطوة السادسة. إصلاح التحذيرات الآلية
وعند تشغيل الرمز، لاحظ أنه يتم استخدام Robolectric.
ويعود السبب في ذلك إلى اختبار AndroidX وبرنامج تشغيل اختبار AndroidJunit4، ويتم ذلك بدون كتابة سطر واحد مباشرةً من رمز Robolectric.
قد تلاحظ تحذيرين.
No such manifest file: ./AndroidManifest.xml
"WARN: Android SDK 29 requires Java 9..."
يمكنك حلّ تحذير No such manifest file: ./AndroidManifest.xml
عن طريق تعديل ملف Gradle.
- أضف السطر التالي إلى ملف gradle حتى يتم استخدام بيان Android الصحيح. يتيح لك الخيار includeAndroidResources الوصول إلى موارد Android في اختبارات وحدتك، بما في ذلك ملف AndroidManifest.
app/build.gradle
// Always show the result of every unit test when running via command line, even if it passes.
testOptions.unitTests {
includeAndroidResources = true
// ...
}
التحذير "WARN: Android SDK 29 requires Java 9..."
أكثر تعقيدًا. إجراء اختبارات على Android Q يتطلب Java 9. بدلاً من محاولة ضبط "استوديو Android" لاستخدام Java 9، في هذا الدرس التطبيقي، احتفِظ بالهدف واجمع حزمة تطوير البرامج (SDK) بنسبة 28.
باختصار:
- يمكن أن تأتي اختبارات النماذج الخالصة عادةً في مجموعة المصدر
test
لأن رمزها لا يتطلب عادةً Android. - يمكنك استخدام مكتبة اختبار Android للحصول على إصدارات تجريبية من مكونات مثل التطبيقات والأنشطة.
- إذا كنت بحاجة إلى تشغيل رمز Android تم محاكاته في مجموعة المصدر
test
، يمكنك إضافة الاعتمادية Robolectric والتعليقات التوضيحية@RunWith(AndroidJUnit4::class)
.
تهانينا، أنت تستخدم كل من مكتبة اختبارات AndroidX وRobolectric لإجراء اختبار. لم ينتهِ الاختبار بعد (لم تكتب بعد بيان التأكيد حتى الآن، وما عليك سوى قول // TODO test LiveData
). وستتعلّم كتابة عبارات التأكيد باستخدام LiveData
بعد ذلك.
في هذه المهمة، ستتعرّف على كيفية تأكيد قيمة LiveData
بشكل صحيح.
هذا هو المكان الذي توقفت عنده بدون addNewTask_setsNewTaskEvent
عرض نموذج الاختبار.
TasksViewmodelTest.kt
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
// TODO test LiveData
}
لاختبار LiveData
، يُنصح بإجراء أمرين:
- استخدام
InstantTaskExecutorRule
- التأكُّد من متابعة
LiveData
الخطوة الأولى: استخدام InstantTaskExecutorRule
InstantTaskExecutorRule
هي قاعدة وحدات. وعند استخدامه مع التعليق التوضيحي @get:Rule
، يؤدي ذلك إلى تشغيل بعض الرموز في الفئة InstantTaskExecutorRule
قبل الاختبارات وبعدها (للاطّلاع على الرمز بالضبط، يمكنك استخدام اختصار لوحة المفاتيح Command+B لعرض الملف).
تعمل هذه القاعدة على تنفيذ جميع مهام الخلفية المتعلقة بـ "مكونات البنية" في سلسلة المحادثات نفسها حتى تتم نتائج الاختبار بشكل متزامن وبترتيب قابل للتكرار. عند كتابة اختبارات تشمل اختبار LiveData، استخدِم هذه القاعدة.
- أضِف تبعية الدرجات لـ مكتبة الاختبار الأساسي لمكونات البنية (التي تحتوي على هذه القاعدة).
app/build.gradle
testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
- فتح
TasksViewModelTest.kt
- أضِف
InstantTaskExecutorRule
داخل الصفTasksViewModelTest
.
TasksViewmodelTest.kt
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
// Other code...
}
الخطوة الثانية: إضافة LiveLiveTestUtil.kt الفئة
تتمثل الخطوة التالية في التأكّد من رصد LiveData
الاختبار.
عند استخدام LiveData
، يكون لديك عادةً نشاط أو جزء (LifecycleOwner
) يراقب LiveData
.
viewModel.resultLiveData.observe(fragment, Observer {
// Observer code here
})
هذه الملاحظة مهمة. أنت بحاجة إلى مراقبين نشطين على LiveData
لتنفيذ
- تشغيل أي أحداث
onChanged
. - تشغيل أي إحالات ناجحة.
للحصول على سلوك LiveData
المتوقع لنموذج العرض LiveData
، يجب ملاحظة LiveData
باستخدام LifecycleOwner
.
يتسبب ذلك في حدوث مشكلة: في اختبار TasksViewModel
، ليس لديك نشاط أو جزء لمراقبة LiveData
. لتجنب ذلك، يمكنك استخدام طريقة observeForever
التي تضمن مراقبة LiveData
باستمرار، بدون الحاجة إلى LifecycleOwner
. عند observeForever
، عليك تذكُّر إزالة المراقب أو خطر تسرّب أداة المراقبة.
يبدو أن هذا الرمز مشابه للرمز أدناه. افحصه:
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// Create observer - no need for it to do anything!
val observer = Observer<Event<Unit>> {}
try {
// Observe the LiveData forever
tasksViewModel.newTaskEvent.observeForever(observer)
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.value
assertThat(value?.getContentIfNotHandled(), (not(nullValue())))
} finally {
// Whatever happens, don't forget to remove the observer!
tasksViewModel.newTaskEvent.removeObserver(observer)
}
}
الكثير من الرموز النموذجية لملاحظة LiveData
واحدة في الاختبار. وهناك بعض الطرق للتخلص من هذه النصوص النموذجية. أنت بصدد إنشاء وظيفة إضافة تُسمى LiveDataTestUtil
لتسهيل إضافة المراقبين.
- أنشئ ملف Kotlin جديدًا باسم
LiveDataTestUtil.kt
في مجموعة مصادرtest
.
- انسخ الرمز أدناه والصقه.
LiveDataTestUtil.kt
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)
try {
afterObserve.invoke()
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
} finally {
this.removeObserver(observer)
}
@Suppress("UNCHECKED_CAST")
return data as T
}
هذه الطريقة معقّدة إلى حدٍّ ما. وتنشئ وظيفة إضافة Kotlin باسم getOrAwaitValue
والتي تضيف مراقبًا وتحصل على قيمة LiveData
ثم تنظِّف أداة الملاحظة - وهي في الأساس نسخة قصيرة وقابلة لإعادة الاستخدام من رمز observeForever
الموضّح أعلاه. للحصول على شرح كامل لهذا الصف، اطّلع على مشاركة المدونة هذه.
الخطوة الثالثة. يُرجى استخدام getOrAPendingValue لكتابة التأكيد.
في هذه الخطوة، يمكنك استخدام الطريقة getOrAwaitValue
وكتابة بيان تأكيد يتحقّق من تشغيل الرمز newTaskEvent
.
- احصل على قيمة
LiveData
لـnewTaskEvent
باستخدامgetOrAwaitValue
.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
- تأكّد من أن القيمة ليست فارغة.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))
من المفترض أن يظهر الاختبار الكامل على النحو التالي:
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.nullValue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
assertThat(value.getContentIfNotHandled(), not(nullValue()))
}
}
- تشغيل الرمز ومشاهدة اجتياز الاختبار!
بعد أن تعرّفت على كيفية كتابة اختبار، اكتب واحدًا بنفسك. في هذه الخطوة، باستخدام المهارات التي تعلمتها، تمرّن على كتابة اختبار TasksViewModel
آخر.
الخطوة الأولى: كتابة اختبار طرق العرض الخاصة بك
ستكتب setFilterAllTasks_tasksAddViewVisible()
. من المفترض أن يتحقق هذا الاختبار في حال ضبط نوع الفلتر على إظهار جميع المهام، وأنّ الزر إضافة مهمة مرئي.
- باستخدام
addNewTask_setsNewTaskEvent()
كمرجع، اكتب اختبارًا فيTasksViewModelTest
باسمsetFilterAllTasks_tasksAddViewVisible()
يضبط وضع الفلترة علىALL_TASKS
وتؤكد أنtasksAddViewVisible
LiveData هوtrue
.
استخدِم الرمز أدناه للبدء.
TasksViewmodelTest
@Test
fun setFilterAllTasks_tasksAddViewVisible() {
// Given a fresh ViewModel
// When the filter type is ALL_TASKS
// Then the "Add task" action is visible
}
ملاحظة:
- تعداد
TasksFilterType
لجميع المهام هوALL_TASKS.
- يتم التحكّم في مستوى ظهور الزر لإضافة مهمة من خلال
LiveData
tasksAddViewVisible.
- إجراء الاختبار.
الخطوة الثانية: مقارنة الاختبار بالحلّ
قارِن حلّك بالحلول أدناه.
TasksViewmodelTest
@Test
fun setFilterAllTasks_tasksAddViewVisible() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When the filter type is ALL_TASKS
tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)
// Then the "Add task" action is visible
assertThat(tasksViewModel.tasksAddViewVisible.getOrAwaitValue(), `is`(true))
}
تحقّق مما إذا كنت تنفّذ ما يلي:
- يمكنك إنشاء
tasksViewModel
باستخدام عبارةApplicationProvider.getApplicationContext()
نفسها التي تعمل بنظام التشغيل Android. - عليك استدعاء الطريقة
setFiltering
، مع تمرير رقم نوع الفلترALL_TASKS
. - تأكد من صحة
tasksAddViewVisible
باستخدام الطريقةgetOrAwaitNextValue
.
الخطوة الثالثة. إضافة قاعدة @before
لاحِظ كيفية تحديد TasksViewModel
في بداية كلا الاختبارين.
TasksViewmodelTest
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
عندما يكون لديك رمز إعداد متكرر لاختبارات متعددة، يمكنك استخدام التعليق التوضيحي @before لإنشاء طريقة إعداد وإزالة الرمز المتكرر. بما أنّ كل هذه الاختبارات ستختبر TasksViewModel
، وتحتاج إلى نموذج عرض، انقل هذا الرمز إلى قسم @Before
.
- أنشِئ متغيّر مثيل
lateinit
باسمtasksViewModel|
. - أنشِئ طريقة باسم
setupViewModel
. - يمكنك التعليق التوضيحي باستخدام
@Before
. - نقل رمز مثيل نموذج العرض إلى
setupViewModel
TasksViewmodelTest
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
- تشغيل الرمز
تحذير
لا تنفِّذ ما يلي، ولا تُعِدّ
tasksViewModel
بتعريفها:
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
سيؤدي هذا الإجراء إلى استخدام المثيل نفسه لجميع الاختبارات. وهذا أمر يجب تجنّبه، لأنه يجب أن يكون لكل اختبار نسخة جديدة من الموضوع قيد الاختبار (وViewView في هذه الحالة).
ويجب أن يظهر الرمز النهائي لـ TasksViewModelTest
على النحو التالي:
TasksViewmodelTest
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Subject under test
private lateinit var tasksViewModel: TasksViewModel
// Executes each task synchronously using Architecture Components.
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
}
@Test
fun addNewTask_setsNewTaskEvent() {
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.awaitNextValue()
assertThat(
value?.getContentIfNotHandled(), (not(nullValue()))
)
}
@Test
fun getTasksAddViewVisible() {
// When the filter type is ALL_TASKS
tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)
// Then the "Add task" action is visible
assertThat(tasksViewModel.tasksAddViewVisible.awaitNextValue(), `is`(true))
}
}
انقر هنا لمعرفة الفرق بين الرمز الذي بدأته والرمز النهائي.
لتنزيل رمز الدرس التطبيقي للترميز، يمكنك استخدام الأمر git أدناه:
$ git clone https://github.com/googlecodelabs/android-testing.git $ cd android-testing $ git checkout end_codelab_1
بدلاً من ذلك، يمكنك تنزيل المستودع كملف Zip وفك ضغطه وفتحه في"استوديو Android".
يتناول هذا الدرس التطبيقي حول الترميز ما يلي:
- طريقة إجراء الاختبارات من "استوديو Android"
- الفرق بين اختبارات
test
(المحلية) واختبارات قياس حالة التطبيق (androidTest
). - طريقة كتابة اختبارات الوحدات المحلية باستخدام JUnit وHamcrest
- إعداد اختبارات طريقة العرض باستخدام مكتبة اختبارات AndroidX.
دورة Udacity:
مستندات مطوّر برامج Android:
- دليل بنية التطبيقات
- JUnit4
- أحمق
- مكتبة الاختبارات الكهربائية
- مكتبة اختبارات AndroidX
- مكتبة الاختبارات الأساسية لمكونات AndroidX
- مجموعات المصادر
- الاختبار من سطر الأوامر
فيديوهات:
غير ذلك:
للحصول على روابط إلى دروس تطبيقية أخرى حول الترميز في هذه الدورة التدريبية، اطّلِع على الصفحة المقصودة للإصدارات المتقدّمة من Android في لغة ترميز Kotlin.