يتم إجراء الاختبار المحلي مباشرةً على محطة العمل الخاصة بك، وليس على جهاز Android أو محاكي. وعلى هذا النحو، فإنها تستخدم آلة Java الافتراضية (JVM) المحلية، بدلاً من جهاز Android لإجراء الاختبارات. تتيح لك الاختبارات المحلية تقييم منطق تطبيقك بسرعة أكبر. ومع ذلك، فإن عدم القدرة على التفاعل مع إطار عمل Android يخلق قيودًا في أنواع الاختبارات التي يمكنك إجراؤها.
يتحقّق اختبار الوحدة من سلوك قسم صغير من الرمز، وهو الوحدة قيد الاختبار. ويتم ذلك من خلال تنفيذ هذا الرمز والتحقّق من النتيجة.
عادةً ما تكون اختبارات الوحدات بسيطة، ولكن قد يكون إعدادها صعبًا عندما لا تكون الوحدة قيد الاختبار مصمَّمة مع وضع قابلية الاختبار في الاعتبار:
- يجب أن يكون الرمز الذي تريد التحقّق منه متاحًا من خلال اختبار. على سبيل المثال، لا يمكنك اختبار طريقة خاصة مباشرة. بدلاً من ذلك، يمكنك اختبار الفئة باستخدام واجهات برمجة التطبيقات العامة.
- لإجراء اختبارات الوحدات في عزلة، يجب استبدال تبعيات الوحدة قيد الاختبار بمكونات تتحكم فيها، مثل المكونات المزيفة أو البدائل الاختبارية الأخرى. ويُعدّ ذلك مشكلة على وجه الخصوص إذا كانت الرموز البرمجية تعتمد على إطار عمل Android.
للتعرّف على استراتيجيات اختبار الوحدات الشائعة في Android، يُرجى الاطّلاع على مقالة ما يجب اختباره.
موقع الاختبارات المحلية
يتم تلقائيًا وضع ملفات المصدر لاختبارات الوحدات المحلية في
module-name/src/test/. يكون هذا الدليل متوفّرًا عند إنشاء مشروع جديد باستخدام "استوديو Android".
إضافة تبعيات الاختبار
تحتاج أيضًا إلى ضبط تبعيات الاختبار لمشروعك لاستخدام واجهات برمجة التطبيقات العادية التي يوفّرها إطار عمل اختبار JUnit.
لإجراء ذلك، افتح ملف build.gradle الخاص بوحدة تطبيقك وحدِّد المكتبات التالية كتبعيات. استخدِم الدالة testImplementation للإشارة إلى أنّها تنطبق على مجموعة رموز المصدر للاختبار المحلي، وليس على التطبيق:
dependencies {
// Required -- JUnit 4 framework
testImplementation "junit:junit:$jUnitVersion"
// Optional -- Robolectric environment
testImplementation "androidx.test:core:$androidXTestVersion"
// Optional -- Mockito framework
testImplementation "org.mockito:mockito-core:$mockitoVersion"
// Optional -- mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
// Optional -- Mockk framework
testImplementation "io.mockk:mockk:$mockkVersion"
}
إنشاء فئة اختبار وحدة محلية
يمكنك كتابة فئة اختبار الوحدة المحلية كفئة اختبار JUnit 4.
لإجراء ذلك، أنشئ فئة تحتوي على طريقة اختبار واحدة أو أكثر، عادةً في
module-name/src/test/. تبدأ طريقة الاختبار بالتعليق التوضيحي @Test وتحتوي على الرمز البرمجي لتنفيذ جانب واحد من المكوّن الذي تريد اختباره والتحقّق منه.
يوضّح المثال التالي كيفية تنفيذ فئة اختبار وحدة محلية. تحاول طريقة الاختبار emailValidator_correctEmailSimple_returnsTrue() التحقّق من isValidEmail()، وهي طريقة داخل التطبيق. ستعرض دالة الاختبار القيمة "صحيح" إذا عرضت isValidEmail() أيضًا القيمة "صحيح".
Kotlin
import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class EmailValidatorTest { @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")) } }
Java
import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; class EmailValidatorTest { @Test public void emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("name@email.com")); } }
يجب إنشاء اختبارات قابلة للقراءة لتقييم ما إذا كانت المكونات في تطبيقك
تعرض النتائج المتوقعة. ننصحك باستخدام مكتبة تأكيدات مثل junit.Assert أو Hamcrest أو Truth. المقتطف أعلاه هو مثال على كيفية استخدام
junit.Assert.
مكتبة Android قابلة للمحاكاة
عند تنفيذ اختبارات الوحدات المحلية، يتضمّن المكوّن الإضافي لنظام Gradle المتوافق مع Android مكتبة تحتوي على جميع واجهات برمجة التطبيقات لإطار عمل Android، بما يتوافق مع الإصدار المستخدَم في مشروعك. تحتوي المكتبة على جميع الطرق والفئات العامة لواجهات برمجة التطبيقات هذه، ولكن تمت إزالة الرمز داخل الطرق. إذا تم الوصول إلى أي من الطرق، فسيقوم الاختبار بطرح استثناء.
يتيح ذلك إنشاء اختبارات محلية عند الإشارة إلى فئات في إطار عمل Android، مثل Context. والأهم من ذلك، أنها تتيح لك استخدام إطار عمل
محاكاة مع فئات Android.
محاكاة تبعيات Android
من المشاكل الشائعة أن تجد أنّ فئة تستخدم مصدر السلاسل النصية. يمكنك الحصول على مصدر السلاسل النصية من خلال استدعاء طريقة getString() في فئة Context. ومع ذلك، لا يمكن للاختبار المحلي استخدام Context أو أي من طرقه لأنها
تنتمي إلى إطار عمل Android. من الأفضل نقل استدعاء getString() خارج الفئة، ولكن هذا ليس عمليًا دائمًا. الحل هو إنشاء نموذج وهمي أو نموذج بديل لـ Context يعرض دائمًا القيمة نفسها عند استدعاء طريقة getString().
باستخدام مكتبة Mockable Android وأُطر المحاكاة، مثل Mockito أو MockK، يمكنك برمجة سلوك المحاكاة لفئات Android في اختبارات الوحدات.
لإضافة كائن وهمي إلى اختبار الوحدة المحلية باستخدام Mockito، اتبع نموذج البرمجة هذا:
- أدرِج تبعية مكتبة Mockito في ملف
build.gradle، كما هو موضّح في إعداد بيئة الاختبار. - في بداية تعريف فئة اختبار الوحدة، أضِف التعليق التوضيحي
@RunWith(MockitoJUnitRunner.class). يخبر هذا التعليق التوضيحي مشغل اختبار Mockito بالتحقق من صحة استخدامك لإطار العمل ويبسط تهيئة الكائنات الوهمية. - لإنشاء عنصر وهمي لتبعية Android، أضِف التعليق التوضيحي
@Mockقبل تعريف الحقل. - لإيقاف سلوك التبعية، يمكنك تحديد شرط وقيمة إرجاع عند استيفاء الشرط باستخدام الطريقتين
when()وthenReturn().
يوضّح المثال التالي كيفية إنشاء اختبار وحدة يستخدم عنصر Context وهمي في Kotlin تم إنشاؤه باستخدام Mockito-Kotlin.
import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
private const val FAKE_STRING = "HELLO WORLD"
@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {
@Mock
private lateinit var mockContext: Context
@Test
fun readStringFromContext_LocalizedString() {
// Given a mocked Context injected into the object under test...
val mockContext = mock<Context> {
on { getString(R.string.name_label) } doReturn FAKE_STRING
}
val myObjectUnderTest = ClassUnderTest(mockContext)
// ...when the string is returned from the object under test...
val result: String = myObjectUnderTest.getName()
// ...then the result should be the expected one.
assertEquals(result, FAKE_STRING)
}
}
لمزيد من المعلومات حول استخدام إطار عمل Mockito، يمكنك الاطّلاع على مرجع واجهة برمجة تطبيقات Mockito وفئة SharedPreferencesHelperTest في الرمز النموذجي. يمكنك أيضًا تجربة الدرس التطبيقي حول الترميز عن اختبار تطبيقات Android.
الخطأ: "لم تتم محاكاة الطريقة ..."
تُظهر مكتبة Mockable Android استثناءً إذا حاولت الوصول إلى أي من
طرقها باستخدام رسالة Error: "Method ... not mocked.
إذا كانت الاستثناءات التي تم طرحها تمثّل مشكلة في الاختبارات، يمكنك تغيير
السلوك بحيث تعرض الطرق إما قيمة فارغة أو صفرًا، بناءً على
نوع القيمة التي تم إرجاعها. لإجراء ذلك، أضِف الإعدادات التالية في ملف build.gradle ذي المستوى الأعلى في مشروعك بلغة Groovy:
android {
...
testOptions {
unitTests.returnDefaultValues = true
}