اختبار الوحدات باستخدام JUnit
اكتبوا اختبارات وحدات سريعة وموثوقة باستخدام JUnit 4. استخدموا @Test و@Before و@After وassertions، واختبروا LiveData باستخدام InstantTaskExecutorRule، وcoroutines باستخدام runTest.
اختبار الوحدات باستخدام JUnit درس مجاني في Android Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Android Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Android Academy 4 دروس في المجموع.
لماذا نكتب الاختبارات؟
الاختبارات ليست رفاهية، بل شبكة أمان:
- اكتشاف الأخطاء قبل أن يكتشفها المستخدمون
- إعادة هيكلة الشيفرة بثقة (إذا نجحت الاختبارات، فلم يتعطل شيء)
- توثيق السلوك المقصود
- منع التراجعات عند إضافة الميزات
تميل الشيفرة المختبرة جيدًا أيضًا إلى أن تكون أفضل تصميمًا؛ فإذا كان اختبارها صعبًا، فمن المحتمل أن يكون التصميم مترابطًا بشدة.
أنواع اختبارات Android
فئتان:
- اختبارات الوحدات (المجلد
test/) — تُشغّل على JVM ولا تحتاج إلى جهاز. سريعة، وتختبر منطق Kotlin/Java العادي. - الاختبارات المزوّدة بالأجهزة (المجلد
androidTest/) — تُشغّل على جهاز أو محاكي. أبطأ، وتختبر الشيفرة الخاصة بـ Android، مثل قاعدة بيانات Room وواجهة المستخدم.
فضّل اختبارات الوحدات لمنطق الأعمال. واستخدم الاختبارات المزوّدة بالأجهزة فقط عند ضرورة التفاعل مع واجهات Android البرمجية.
إعداد JUnit 4
يُضمّن JUnit 4 افتراضيًا في مشاريع Android. أضف ما يلي إلى app/build.gradle:
// app/build.gradle:
dependencies {
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
testImplementation 'androidx.arch.core:core-testing:2.2.0' // for LiveData
}أول اختبار JUnit لك
أنشئ فئة في src/test/java/، وعلّم دوال الاختبار بالتعليق التوضيحي @Test:
import org.junit.Test
import org.junit.Assert.*
class CalculatorTest {
private val calc = Calculator()
@Test
fun `addition returns correct sum`() {
val result = calc.add(2, 3)
assertEquals(5, result)
}
@Test
fun `division by zero throws exception`() {
assertThrows(ArithmeticException::class.java) {
calc.divide(10, 0)
}
}
}@Before و@After
استخدم التعليقات التوضيحية لدورة الحياة لإعداد الحالة وتنظيفها حول كل اختبار:
class UserRepositoryTest {
private lateinit var repo: UserRepository
private lateinit var fakeDao: FakeUserDao
@Before
fun setUp() {
fakeDao = FakeUserDao() // fresh instance before each test
repo = UserRepository(fakeDao)
}
@After
fun tearDown() {
fakeDao.clear() // clean up after each test
}
@Test
fun `getUsers returns all users`() {
fakeDao.insert(User(1, "Alice"))
fakeDao.insert(User(2, "Bob"))
assertEquals(2, repo.getUsers().size)
}
}التأكيدات الشائعة
يوفّر JUnit دوال التأكيد ضمن Assert.*:
// Equality:
assertEquals(expected, actual)
assertNotEquals(unexpected, actual)
// Null:
assertNull(value)
assertNotNull(value)
// Boolean:
assertTrue(condition)
assertFalse(condition)
// Same object reference:
assertSame(expected, actual)
// Custom message on failure:
assertEquals("User ID should be 1", 1, user.id)
// Exception:
assertThrows(IllegalArgumentException::class.java) {
User(id = -1, name = "")
}اختبار ViewModel
لاختبار LiveData، أضف InstantTaskExecutorRule كي تنشر LiveData النتائج بشكل متزامن:
class UserViewModelTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val fakeRepo = FakeUserRepository()
private lateinit var viewModel: UserViewModel
@Before
fun setUp() {
viewModel = UserViewModel(fakeRepo)
}
@Test
fun `users are loaded on init`() {
fakeRepo.usersToReturn = listOf(User(1, "Alice"), User(2, "Bob"))
viewModel.loadUsers()
val result = viewModel.users.value
assertNotNull(result)
assertEquals(2, result?.size)
}
}اختبار Coroutines
استخدم TestCoroutineDispatcher / StandardTestDispatcher للتحكم في تنفيذ coroutines أثناء الاختبارات:
class SyncWorkerTest {
@get:Rule
val instantTaskRule = InstantTaskExecutorRule()
private val testDispatcher = StandardTestDispatcher()
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `sync completes successfully`() = runTest {
val repo = FakeRepository()
val worker = SyncUseCase(repo, testDispatcher)
val result = worker.run()
assertTrue(result.isSuccess)
}
}Fake أم Mock
استراتيجيتان لاستبدال الاعتماديات الحقيقية في الاختبارات:
- Fake — تنفيذ عملي مصمم للاختبار، مثل قائمة داخل الذاكرة بدلًا من قاعدة بيانات حقيقية. سهل الكتابة، واضح القراءة، ولا يحتاج إلى مكتبة.
- Mock — كائن بديل يُنشأ تلقائيًا، ويسجل الاستدعاءات ويتيح لك التحقق من التفاعلات، مثل Mockito وMockK. أقوى، لكنه قد يصبح هشًا عند الإفراط في استخدامه.
فضّل Fakes في الحالات البسيطة، واستخدم Mocks عندما تحتاج إلى التحقق من استدعاءات الدوال.
كتابة Fake
يطبّق Fake الواجهة نفسها التي تطبقها الفئة الحقيقية، لكنه يستخدم تخزينًا داخل الذاكرة:
interface UserDao {
fun getAll(): List<User>
fun insert(user: User)
}
class FakeUserDao : UserDao {
private val storage = mutableListOf<User>()
override fun getAll(): List<User> = storage.toList()
override fun insert(user: User) { storage.add(user) }
fun clear() { storage.clear() }
}اصطلاح تسمية الاختبارات
تصف أسماء الاختبارات الجيدة ما يجري اختباره والنتيجة المتوقعة. استخدم أسماء الدوال بين علامتي backtick في Kotlin للحصول على أوصاف سهلة القراءة:
`getUsers returns empty list when no users exist``login throws exception when password is blank``add returns correct sum for negative numbers`
النمط: `subject does X when Y`
تحقق سريع
ما الغرض من @Before في فئة اختبار JUnit؟
مراجعة: اختبار الوحدات باستخدام JUnit
التطبيقات الموثوقة هي تطبيقات مختبرة:
- توضع اختبارات الوحدات في
src/test/وتُشغّل على JVM، لذا فهي سريعة - استخدم
@TestوassertEqualsوassertTrueوassertThrows - استخدم
@Before/@Afterللإعداد والتنظيف - استخدم
InstantTaskExecutorRuleلاختبار LiveData بشكل متزامن - استخدم
runTest+StandardTestDispatcherلاختبار coroutine - فضّل Fakes على Mocks للاستبدالات البسيطة
التالي: المحاكاة باستخدام Mockito للتحقق من التفاعلات.
الأسئلة الشائعة
هل درس «اختبار الوحدات باستخدام JUnit» مجاني؟
نعم — نص درس «اختبار الوحدات باستخدام JUnit» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Android Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Android Academy 4 دروس في المجموع.
ماذا ستتعلم في «اختبار الوحدات باستخدام JUnit»؟
اكتبوا اختبارات وحدات سريعة وموثوقة باستخدام JUnit 4. استخدموا @Test و@Before و@After وassertions، واختبروا LiveData باستخدام InstantTaskExecutorRule، وcoroutines باستخدام runTest. تتمرن على Android Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Android Academy؟
لا تُشترط خبرة سابقة. Android Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «اختبار الوحدات باستخدام JUnit»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Android Academy هذا؟
نعم. كل درس في Android Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- اختبار الوحدات باستخدام JUnit
- المحاكاة باستخدام Mockito
- اختبار الواجهة باستخدام Espresso
- تصحيح الأخطاء وتحليل الأداء