Mock — bu nədir, mock-obyektlər və testlər üçün kitabxanalar

Müəllif: IT Sectr Dərc olunub: 2026-04-10 Oxuma vaxtı: 8 dəq

Mock — real komponentin davranışını təqlid edən və onunla qarşılıqlı əlaqəni yoxlamağa imkan verən əvəzedici obyektdir. Sadəcə verilmiş dəyəri qaytaran Stub-dan fərqli olaraq, Mock metodun çağırılma faktını, ötürülən arqumentləri və çağırışların sayını qeyd edir. Mockito (2024) məlumatlarına görə, Mock Java və Kotlin layihələrində ən populyar Test Double növüdür və mobil tətbiqlərin vahid testlərinin 70%-dən çoxunda istifadə olunur.

Əsas məqamlar

  • Mock — qarşılıqlı əlaqəni yoxlayan obyekt: hansı metodlar çağırıldı, hansı arqumentlərlə və neçə dəfə
  • Mockito — Java və Android layihələrində Mock yaratmaq üçün ən populyar kitabxana
  • MockK — Kotlin üçün Mockito-ya alternativ, korutinlər və sealed class üçün dəstək ilə
  • Behavior verification — Mock-un Stub-dan əsas fərqi: Mock davranışı yoxlayır, vəziyyəti yox
  • Over-mocking — əsas Anti-Pattern: mock yalnız xarici asılılıqlar üçün olmalıdır

Mock nədir?

Mock — mocking freymvorku (Mockito, MockK, EasyMock) tərəfindən yaradılan, interfeysi və ya sinfi təqlid edən və bütün metod çağırışlarını qeydə alan obyektdir. Proqramçı gözləntiləri təyin edir: X metodu Y arqumentləri ilə çağırılacaq və Z qaytaracaq. Test icra edildikdən sonra Mock gözləntilərin real çağırışlarla uyğun olub-olmadığını yoxlayır.

Termin Test Doubles-in teatr metaforasından gəlir: Mock — sadəcə səhnədə dayanmayan (Dummy kimi), rol oynayan və onunla düzgün qarşılıqlı əlaqə qurulduğunu yoxlayan “təqlidçi”dir. Əgər test edilən kod Mock-un gözlədiyi metodu çağırmadısa və ya səhv arqumentlərlə çağırdısa — test pozulmuş gözlənti haqqında mesajla uğursuz olur.

Mock necə işləyir

Mock freymvorkun fabriki vasitəsilə yaradılır: mockk<MyInterface>() və ya Mockito.mock(MyClass.java). Freymvork bütün metod çağırışlarını ələ keçirən proxy-obyekt yaradır. Hər çağırış əvvəlcədən təyin edilmiş gözləntilərlə (expectations) müqayisə olunur. Çağırış gözləntiyə uyğundursa — verilmiş dəyər qaytarılır. Əks halda — Mock konfiqurasiyadan asılı olaraq standart dəyər qaytarır və ya istisna atır.

Mock nə vaxt zəruridir

Mock test edilən kod yan təsirləri olan komponentlərlə qarşılıqlı əlaqədə olduqda məcburidir: məlumatların serverə göndərilməsi, verilənlər bazasına yazı, loglama, analitika, naviqasiya, sistem dialoqlarının göstərilməsi. Mock olmadan bu qarşılıqlı əlaqələri real infrastrukturu işə salmadan yoxlamaq mümkün deyil. Google Testing Blog-a görə, Mock tətbiqin analitik hadisəni həqiqətən göndərdiyini test serveri qaldırmadan yoxlamağın yeganə yoludur.

Mock və Stub: ətraflı müqayisə

MockStub arasındakı fərq testlərdə ən çox müzakirə olunan mövzulardan biridir. Hər iki növ real asılılığı əvəz edir, lakin prinsipial olaraq fərqli üsullarla.

KriteriyaMockStub
Əsas sualMetod çağırıldı?Hansı nəticə qaytarıldı?
YoxlamaDavranış (verify)Vəziyyət (assert)
Məlumat qaytarmaİstəyə bağlıMəcburi
Nümunəverify(analytics).logEvent("click")assertEquals(5, repository.getCount())
Nə vaxt istifadə olunurYan təsirlərMəlumat qaytarma

Praktik qayda: Mock ya yox

Seçim üçün sadə test: özünüzdən soruşun — “bu kod sətrini silsəm, test uğursuz olacaq?”. Əgər test qaytarılan dəyəri yoxlayırsa — Stub lazımdır (assert ilə yoxlama). Əgər test kodun metodu düzgün arqumentlərlə çağırdığını yoxlayırsa — Mock lazımdır (verify ilə yoxlama). Bu dixotomiya Command-Query Separation nümunəsindən irəli gəlir: vəziyyəti dəyişən metodlar (commands) Mock tələb edir; məlumat qaytaran metodlar (queries) Stub tələb edir.

Mockito və MockK: kitabxanaların müqayisəsi

MockitoMockK arasında seçim Kotlin-də Android layihəsinin test yığınının qurulmasında ilk qərarlardan biridir. Hər iki kitabxana eyni vəzifəni yerinə yetirir, lakin Kotlin spesifikasına fərqli yanaşmalarla.

Mockito: sübut olunmuş klassika

Mockito — Java layihələri üçün de-fakto standartdır. 5.x versiyası daxili MockMaker sayəsində final siniflər, statik metodlar və konstruktorlar üçün mock-obyektləri dəstəkləyir. Kotlin layihələri üçün Mockito əlavə konfiqurasiya tələb edir: təkmilləşdirilmiş sintaksis üçün mockito-kotlin genişlənmələri, final siniflər üçün mockito-inline. Mockito əlavə adapterlər olmadan Kotlin korutinlərini və suspend funksiyalarını dəstəkləmir.

MockK: Kotlin-first yanaşması

MockK xüsusi olaraq Kotlin üçün yaradılmışdır. O, korutinləri (coEvery, coVerify), sealed class, data class, object-sinqletonları və genişləndirmə funksiyalarını nativ dəstəkləyir. MockK sintaksisi lambda blokları ilə DSL-dən istifadə edir ki, bu da Kotlin kodunda təbii görünür. MockK həmçinin əlavə konfiqurasiya olmadan xassələri (property mocking) mock edə bilir — bu LiveData, StateFlow və Delegates istifadə edən Android layihələri üçün vacibdir.

kotlin
// Mockito + mockito-kotlin
val repository = mock<UserRepository>()
whenever(repository.getUser(1)).thenReturn(user)

// MockK
val repository = mockk<UserRepository>()
every { repository.getUser(1) } returns user

Məhsuldarlıq müqayisəsi

Benchmarklər (JVM Benchmark, 2024) göstərir ki, MockK Kotlin layihələri üçün mock-obyektləri Mockito-dan 15–20% daha sürətli yaradır, çünki Java Reflections deyil, birbaşa Kotlin bytecode ilə işləyir. Minlərlə vahid testi olan layihələr üçün tikinti vaxtı fərqi nəzərə çarpa bilər: MockK böyük layihələrdə testlərin tam icrasında 30–60 saniyə qənaət edir.

Kotlin-də Mock test nümunələri

Üç ssenarini nəzərdən keçirək: Mock asılılıqları ilə ViewModel-in test edilməsi, API çağırışının yoxlanılması ilə UseCase-in test edilməsi və coVerify ilə korutinlərin test edilməsi.

Nümunə 1: Mock analitika ilə ViewModel

kotlin
class ProfileViewModelTest {
    private val analytics = mockk<AnalyticsService>()
    private val repo = mockk<UserRepository>()
    private val vm = ProfileViewModel(repo, analytics)

    fun `profile opened logs analytics event`() {
        every { analytics.logEvent("profile_opened") } returns Unit

        vm.onViewCreated()

        verify { analytics.logEvent("profile_opened") }
    }
}

Nümunə 2: Asinxron yoxlama ilə UseCase

kotlin
class SendMessageUseCaseTest {
    private val api = mockk<MessagingApi>()
    private val useCase = SendMessageUseCase(api)

    fun `send message with correct payload`() = runTest {
        val message = Message(text = "Hello", userId = 42)

        coEvery { api.sendMessage(any()) } returns MessageResult.Sent("msg_1")

        val result = useCase.execute(message)

        coVerify {
            api.sendMessage(match {
                it.text == "Hello" && it.userId == 42
            })
        }
        assertTrue(result is MessageResult.Sent)
    }
}

Nümunə 3: ArgumentCaptor ilə arqumentlərin yoxlanılması

kotlin
class OrderUseCaseTest {
    private val api = mockk<OrderApi>()
    private val useCase = OrderUseCase(api)
    private val slot = slot<OrderRequest>()

    fun `order request contains correct items`() = runTest {
        coEvery { api.placeOrder(capture(slot)) } returns OrderResult.Placed("order_1")

        useCase.execute(listOf("item_a", "item_b"))

        assertEquals(2, slot.captured.items.size)
        assertEquals("item_a", slot.captured.items[0])
    }
}

Mock testlərin ən yaxşı təcrübələri

Mobil inkişafda Mock-dan effektiv istifadə intizam tələb edir. Bu qaydaların pozulması testləri hər refaktorinqdə sınan kövrək maneələrə çevirir.

Yalnız tətbiqin xarici sərhədlərini mock edin

Sərt qayda: Mock yalnız tətbiqin sərhədini keçən asılılıqlar üçün yaradılır: API müştəriləri, verilənlər bazaları, fayl sistemi, sistem xidmətləri (LocationManager, BluetoothAdapter, Camera). Tətbiqin daxili sinifləri — domen varlıqları, Value Object, sadə utilitlər — Mock ilə əvəz edilməməlidir. Onların davranışı real obyektlər vasitəsilə test edilir.

Hər testdə bir assert/verify

Hər test dəqiq bir məntiqi yoxlama ehtiva etməlidir — ya verify (Mock üçün), ya da assert (Stub üçün). Bir testdə vəziyyət və davranış yoxlamasını qarışdırmayın. Həm API çağırışını, həm də nəticəni yoxlamaq lazımdırsa — müxtəlif adlarla iki ayrı test yaradın. “Hər testdə bir assert” kimi tanınan bu qayda Kent Beck-in (2002) tövsiyələrindən qaynaqlanır.

  • MockK-da relaxUnitFun = true istifadə edin Unit qaytaran metodlar üçün — əks halda Mock təsvir olunmamış çağırışda istisna atacaq
  • Verify-ni yalnız kritik çağırışlarla məhdudlaşdırın — hər getter və setteri yoxlamayın, bu testləri kövrək edir
  • ArgumentMatchers-i mənalı istifadə edin — arqument biznes məntiqi üçün kritikdirsə, any() vacib detalları gizlədir
  • verifyNoMoreInteractions-dan sui-istifadə etməyin — bu metod testi istehsal kodundakı hər dəyişikliyə qarşı lazımsız dərəcədə sərt edir
  • @MockkAnnotations istifadə edin Mock-obyektlərin avtomatik initallaşdırılması üçün — bu boilerplate-i azaldır və oxunaqlılığı yaxşılaşdırır

Mock testlərin qabaqcıl texnikaları

Əsas mocking-dən əlavə, mobil inkişafda spesifik problemləri həll edən qabaqcıl texnikalar mövcuddur: çoxthreadliliyin test edilməsi, Flow vəziyyətinin yoxlanılması və real obyektlərin qismən mock edilməsi.

SpyK ilə Partial Mock

Spy (və ya partial mock) çağırışları real implementasiyaya yönləndirən, lakin ayrı-ayrı metodları ləğv etməyə imkan verən obyekt yaratmağa imkan verir. MockK-da spyk sinfin real instansiyası əsasında yaradılır: val repo = spyk(InMemoryUserRepository()). every vasitəsilə gözləntilər təyin edilmiş çağırışlar Mock-dan keçir; qalanları — real obyektdən. Spy xüsusilə asılılıq inyeksiyası hələ tətbiq edilməmiş legacy-kodun test edilməsi üçün faydalıdır, yalnız bir metodu ləğv etmək lazım olduqda.

Turbine ilə StateFlow-un test edilməsi

Jetpack Compose-da müasir Android layihələrində ViewModel vəziyyəti StateFlow vasitəsilə açıqlayır. MockK Flow asılılıqlarını mock etməyə imkan verir, Turbine kitabxanası isə emissiyanın yoxlanılmasını sadələşdirir. Klassik nümunə: Flow qaytaran UseCase üçün MockK, ViewModel emissiyasının yoxlanılması üçün Turbine. Bu yığın Kotlin Coroutines üzrə Android Testing (Google, 2024) sənədləşməsi tərəfindən tövsiyə olunur.

kotlin
class SearchViewModelTest {
    private val searchUseCase = mockk<SearchUseCase>()
    private val vm = SearchViewModel(searchUseCase)

    fun `search emits results`() = runTest {
        coEvery { searchUseCase.search("android") } returns
            flowOf(SearchResult.Success(listOf(Item("Android TDD"))))

        vm.search("android")

        vm.state.test {
            val state = awaitItem()
            assertTrue(state.items.isNotEmpty())
            cancelAndIgnoreRemainingEvents()
        }
    }
}

Tez-tez verilən suallar

Mock Mockito-dan nə ilə fərqlənir?

Mock — bu konsepsiya, davranışı yoxlayan Test Double növüdür. Mockito — Java və Android-də Mock-obyektlər yaratmaq üçün kitabxanadır. Digər kitabxanalar: MockK (Kotlin), EasyMock (Java), Cuckoo (iOS).

Mock Kotlin korutinləri ilə necə işləyir?

Suspend funksiyalarını Mock ilə test etmək üçün MockK (coEvery / coVerify) və ya mockito-kotlin ilə Mockito istifadə edin. MockK korutinləri nativ dəstəkləyir: coEvery suspend funksiyasının davranışını təyin edir, coVerify onun korutin daxilində çağırılmasını yoxlayır. Bütün suspend çağırışları runTest (kotlinx-coroutines-test) daxilində icra edilməlidir.

Mock təkrar çağırışlarda fərqli dəyərlər qaytara bilərmi?

Bəli. MockK-da bunun üçün returnsMany istifadə olunur: every { api.getData() } returnsMany listOf(response1, response2). Mockito-da — thenReturn(value1).thenReturn(value2) zənciri. Bu, ardıcıl çağırışlarda müxtəlif cavablarla davranışın test edilməsi üçün faydalıdır.

Testlər arasında Mock vəziyyətini necə təmizləmək olar?

MockK-da @MockK annotasiyasını relaxed = true sahəsi ilə istifadə edin və @After metodunda clearMocks(mock) çağırın. Mockito-da — Mockito.reset(mock). Ən yaxşı təcrübə: testlər arasında təsiri istisna etmək üçün hər test üçün @Before vasitəsilə yeni Mock yaradın.

Mock Kotlin-də sealed class-ı necə emal edir?

MockK sealed class ilə düzgün işləyir: every { useCase() } returns Result.Success(data). Mockito sealed class-ı birbaşa dəstəkləmir, dolayı yollar tələb edir. Bu, Kotlin layihələri üçün Mockito əvəzinə MockK-ın tövsiyə edilməsinin səbəblərindən biridir.

Nəticə

  • Mock — asılılıqların vəziyyətini (assert) deyil, davranışını (verify) yoxlayan Test Double növü
  • Mockito — Java/Android üçün standart, MockK — korutin və sealed class dəstəyi ilə Kotlin-first seçim
  • Əsas qayda: Mock xarici sərhədlər üçün (şəbəkə, VB, sistem xidmətləri), real obyektlər daxili siniflər üçün
  • Over-mocking — əsas Anti-Pattern: həddindən artıq asılılıq əvəzləməsi testləri kövrək və az faydalı edir
  • Bir test — bir məntiqi yoxlama: Mock üçün verify və ya Stub üçün assert, lakin bir testdə hər ikisi deyil
  • ArgumentCaptor / slot — kor any() əvəzinə Mock çağırış arqumentlərini yoxlamaq üçün düzgün üsul
  • MockK Kotlin layihələri üçün tövsiyə olunur: coEverycoVerify əlavə adapterlər olmadan korutinlərlə nativ işləyir

Açar təslim mobil tətbiq hazırlayacağıq

IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.

Layihəni müzakirə et

Həm də oxuyun