Mockk لـ Kotlin: دليل شامل مع أمثلة تطبيقية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة عن Mockk: ما هي ولماذا تحتاجها

Mockk هي مكتبة حديثة لإنشاء المحاكاة (mock objects) في لغة البرمجة Kotlin. صُممت خصيصًا لتراعي خصائص Kotlin الفريدة مثل: القابلية للصفر (nullability)، دوال التوسيع (extension functions)، coroutines، الكائنات المرافقة (companion objects)، وغيرها من الميزات غير الموجودة في Java.

لماذا تحتاج مكتبة Mockk؟ عند كتابة اختبارات الوحدة (unit tests)، غالبًا ما نواجه موقفًا تعتمد فيه الفئة المختبرة على فئات أخرى، خدمات، قواعد بيانات، أو واجهات برمجية خارجية (APIs). لعزل الكود المختبر وجعل الاختبارات سريعة وموثوقة ومستقلة عن العوامل الخارجية، نقوم بإنشاء "mock objects" — وهي كائنات وهمية تحاكي سلوك التبعيات الحقيقية.

أهم إمكانيات Mockk:

  • إنشاء mock للفئات، الواجهات، والكائنات
  • دعم التعليقات التوضيحية (@MockK, @RelaxedMockK, @SpyK)
  • العمل مع coroutines (coEvery, coVerify)
  • محاكاة دوال التوسيع (extension functions)
  • محاكاة الطرق الثابتة والكائنات المرافقة
  • دعم الفتحات (slot) لالتقاط الوسائط
  • دعم المحاكاة الجزئية (spy)

تثبيت Mockk

يتم توزيع Mockk عبر Maven Central. لإضافتها إلى مشروعك، أضف التبعية في ملف build.gradle.kts (لـ Gradle مع Kotlin DSL):

// build.gradle.kts
dependencies {
    testImplementation("io.mockk:mockk:1.13.10")
}

لـ Gradle مع Groovy DSL:

// build.gradle
dependencies {
    testImplementation "io.mockk:mockk:1.13.10"
}

لـ Maven:

<dependency>
    <groupId>io.mockk</groupId>
    <artifactId>mockk</artifactId>
    <version>1.13.10</version>
    <scope>test</scope>
</dependency>

إذا كنت تستخدم coroutines، لا حاجة لإضافة أي تبعيات إضافية — دعم coroutines مدمج في Mockk.

بداية سريعة مع مثال عملي

لنأخذ مثالًا بسيطًا. لنفترض أن لدينا خدمة UserService تعتمد على مستودع UserRepository:

// الفئات المراد اختبارها
class UserRepository {
    fun findById(id: Long): String {
        // التنفيذ الحقيقي يتصل بقاعدة البيانات
        return "مستخدم حقيقي"
    }
}

class UserService(private val repository: UserRepository) {
    fun getUserName(id: Long): String {
        val user = repository.findById(id)
        return if (user.isNotEmpty()) user else "غير معروف"
    }
}

الآن لنكتب اختبارًا باستخدام Mockk:

import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.jupiter.api.Test

class UserServiceTest {

    @Test
    fun `getUserName يجب أن يعيد اسم المستخدم من المستودع`() {
        // 1. إنشاء mock للمستودع
        val mockRepository = mockk<UserRepository>()
        
        // 2. تحديد سلوك الـ mock
        every { mockRepository.findById(1L) } returns "أحمد علي"
        
        // 3. إنشاء الكائن المختبر مع الـ mock
        val service = UserService(mockRepository)
        
        // 4. استدعاء الطريقة المختبرة
        val result = service.getUserName(1L)
        
        // 5. التحقق من النتيجة
        assert(result == "أحمد علي")
        
        // 6. التحقق من أن الطريقة استُدعيت مرة واحدة بالضبط
        verify(exactly = 1) { mockRepository.findById(1L) }
    }
}

الطرق والدوال الأساسية في Mockk

سنستعرض 10 طرق رئيسية في المكتبة مع شرح مفصل وأمثلة.

1. mockk() — إنشاء كائن mock

الوظيفة: تنشئ كائن mock للفئة أو الواجهة المحددة. جميع طرق الـ mock تعيد قيمًا افتراضية (null، 0، false) افتراضيًا، وتلقي استثناءً إذا لم يتم تحديد السلوك.

المعاملات:

  • cls: KClass<T>

توصيات