مقدمة: لماذا الاختبار المتقدم هو أساس كود Kotlin الموثوق
في تطوير Kotlin الحديث، كتابة الكود ليست سوى نصف المهمة. الإتقان الحقيقي يظهر في القدرة على إنشاء أنظمة موثوقة وقابلة للاختبار وسهلة الصيانة. اختبارات الوحدة الأساسية مألوفة للجميع، ولكن عندما ينمو المشروع إلى بنية الخدمات المصغرة أو منطق الأعمال المعقد، تصبح الفحوصات البسيطة غير كافية. يتضمن الاختبار المتقدم ليس فقط عزل الفئات، ولكن أيضًا التحقق من التفاعل بين المكونات (اختبارات التكامل) وإعادة إنتاج سيناريوهات المستخدم الكاملة (E2E).
هذه المقالة مخصصة للمطورين الذين هم بالفعل على دراية بأساسيات Kotlin ويريدون رفع جودة مشاريعهم إلى المستوى التالي. سنقوم بتحليل ثلاثة مستويات رئيسية للاختبار: الوحدة والتكامل و E2E، باستخدام أدوات حديثة: MockK و Testcontainers و Ktor Client. ستتعلم كيفية كتابة اختبارات لا "تنجح" فحسب، بل تحمي كودك حقًا من الانحدارات.
1. اختبار الوحدة: العزل والكائنات الوهمية مع MockK
اختبارات الوحدة هي أساس هرم الاختبار. على المستوى المتقدم، لا نتحقق فقط من الدوال، بل نختبر التفاعلات المعقدة بين الكائنات باستخدام الكائنات الوهمية. في Kotlin، أفضل مكتبة لذلك هي MockK.
1.1. الإمكانيات المتقدمة لـ MockK
على عكس Mockito، تمت كتابة MockK خصيصًا لـ Kotlin ويدعم coroutines و lambdas و extension-functions. لنلق نظرة على مثال لاختبار خدمة تستخدم مستودعًا و coroutines:
// الخدمة المراد اختبارهاclass UserService(private val repo: UserRepository) { suspend fun getUserName(id: Long): String { val user = repo.findById(id) return user?.name ?: "Unknown" }}
// الاختبارclass UserServiceTest { private val repo = mockk<UserRepository>() private val service = UserService(repo)
@Test fun `should return user name when user exists`() = runTest { // given coEvery { repo.findById(1L) } returns User(1, "Alice")
// when val result = service.getUserName(1L)
// then assertEquals("Alice", result) coVerify(exactly = 1) { repo.findById(1L) } }
@Test fun `should return Unknown when user not found`() = runTest { // given coEvery { repo.findById(any()) } returns null
// when val result = service.getUserName(99L)
// then assertEquals("Unknown", result) }}النقاط الرئيسية:
coEveryوcoVerify— للعمل مع الدوال من نوع suspend.mockk()ينشئ كائنًا وهميًا فارغًا،spyk()— كائنًا وهميًا جزئيًا لكائن حقيقي.- استخدم
slotsلالتقاط الوسائط.
1.2. اختبار lambdas و extension-functions
يسمح MockK بإنشاء كائنات وهمية لدوال الامتداد، وهو أمر بالغ الأهمية لأطر العمل مثل Ktor أو Spring WebFlux:
class StringValidator { fun String.isValidEmail(): Boolean = this.contains("@")}
// اختبار@Testfun `test extension mock`() { val validator = mockk<StringValidator>() with(validator) { every { "test@mail.com".isValidEmail() } returns true every { "invalid".isValidEmail() } returns false } assertTrue("test@mail.com".isValidEmail()) assertFalse("invalid".isValidEmail())}2. اختبار التكامل: قاعدة البيانات و Testcontainers
تتحقق اختبارات التكامل من كيفية تفاعل كودك مع التبعيات الحقيقية: قواعد البيانات، قوائم انتظار الرسائل، واجهات API الخارجية. هنا تأتي Testcontainers للمساعدة — وهي مكتبة تقوم بتشغيل حاويات Docker مباشرة أثناء الاختبارات.
2.1. إعداد Testcontainers مع PostgreSQL
لتطبيق Spring Boot، يبدو الأمر كما يلي:
المدونات
توصيات الكتب
اقرأ أيضًا