Spy — bu nima, Mockito.spy() va chaqiruvlarni tekshirish

Muallif: IT Sectr Nashr etilgan: 2026-04-10 O'qish vaqti: 9 daq

Spy (ayg'oqchi) — real namunani o'rab oladigan va har bir chaqiruv haqida ma'lumot yozadigan test obyekti: qaysi metodlar chaqirildi, qanday argumentlar bilan, necha marta. Mock-dan farqli o'laroq, spy o'ralgan obyektning real implementatsiyasidan foydalanadi — chaqiruvlar haqiqiy koddan o'tadi, spy esa faqat faktlarni qayd etadi. Test bajarilgandan so'ng, dasturchi ayg'oqchining yozuvlarini tekshiradi: «sendAnalytics metodi uch marta chaqirildimi?». Batafsil — Android Testlash Qo'llanmasida.

Asosiy fikrlar

  • Spy — real implementatsiyani almashtirmasdan uning metod chaqiruvlarini yozib oluvchi obyekt
  • Tekshirish — testdan so'ng spy metod necha marta va qanday argumentlar bilan chaqirilganini tekshirishga imkon beradi
  • Mockito.spy() — Android-da real Java/Kotlin obyektlari uchun ayg'oqchi yaratadi
  • Qisman mocking — spy stub bilan birlashtirilishi mumkin: ba'zi metodlarni tutib olish, boshqalarini o'tkazib yuborish
  • iOS — OCMock (Objective-C) va Swift-da protokollar orqali qo'lda ayg'oqchilar

Spy nima va Mock-dan qanday farq qiladi?

Spy — real obyekt atrofidagi o'ram bo'lib, barcha metod chaqiruvlarini tutib oladi va yozib boradi. Obyektning real mantig'i bajariladi: agar metod ma'lumotlarni saqlasa, qiymatni hisoblasa yoki so'rov yuborsa — hammasi odatdagidek sodir bo'ladi. Qo'shimcha ravishda, spy metama'lumotlarni qayd etadi: metod nomi, argumentlar, chaqiruvlar soni, bajarilish vaqti. Bu atama Mezaros (2007) tasnifiga kiradi va Martin Faulerning „Mocks Aren't Stubs“ maqolasida batafsil tasvirlangan.

Mock-dan asosiy farq — mock obyektni to'liq test qaralama bilan almashtiradi, barcha metodlar sukut bo'yicha hech narsa qilmaydi. Spy mavjud obyektni o'rab oladi: barcha metodlar sukut bo'yicha odatdagidek ishlaydi, lekin bir vaqtning o'zida qayd etiladi. Bu farq prinsipialdir: mock test qilinayotgan kodni haqiqatdan izolyatsiya qiladi, spy esa haqiqatni saqlaydi va uni kuzatishga imkon beradi. Ularning o'rtasidagi tanlov nima test qilinayotganiga bog'liq.

Spy qachon to'g'ri tanlovdir

Spy — to'g'ri tanlov — agar test qilinayotgan kod real obyektning holatini o'zgartirsa va test ham natijani (holatni) tekshirishi, ham chaqiruvlarning to'g'ri tartibda amalga oshirilganiga ishonch hosil qilishi kerak bo'lsa. Mock mos kelmaydi, chunki real implementatsiyani bajarmaydi. Stub mos kelmaydi, chunki chaqiruvlarni qayd etmaydi. Spy bir vaqtning o'zida real mantiqni saqlaydigan va chaqiruvlar haqida ma'lumot beradigan yagona test dublidir.

Spy vs Mock: qachon ayg'oqchidan foydalanish

Mock — to'liq izolyatsiya. Agar test real obyektning implementatsiyasiga bog'liq bo'lmasligi kerak bo'lsa (masalan, ma'lumotlar bazasi yoki tarmoq klienti), mock dan foydalaning. Mock hech qanday chaqiruvning real komponentga yetib bormasligini kafolatlaydi. Bu xavfsiz va bashorat qilish mumkin. Kamchilik: mock real mantiqni bajarmaydi, shuning uchun test qilinayotgan kod qaytarilgan qiymatga tayansa — uni when/stub orqali aniq konfiguratsiya qilish kerak.

Spy — real mantiq + kuzatish. Agar test qilinayotgan kod faqat ma'lumot emas, balki mantig'i test uchun muhim bo'lgan obyekt bilan o'zaro ta'sir qilsa — spy dan foydalaning. Masalan, hodisalarni to'playdigan va vaqti-vaqti bilan yuboradigan AnalyticsTracker. Test hodisalarning buferga qo'shilganini va yuborishdan keyin buferning tozalanganini tekshiradi. Mock buni tekshira olmaydi, chunki tracker-ning real mantig'ini bajarmaydi.

StsenariySpyMock
Real mantiq kerakHaYo'q (qaralama)
Chaqiruvlarni tekshirishHa (son, argumentlar)Ha (son, argumentlar)
Qisman stublashHa (ba'zi metodlar — spy, boshqalari — stub)Yo'q (barcha metodlar — qaralama)
Yon ta'sirlar xavfiYuqori (real kod)Nol
TezlikPastroq (real mantiq)Yuqoriroq (qaralamalar)
O'qilishlikPastroq (nimaning real ekanligini tushunish qiyin)Yuqoriroq (hamma narsa aniq)

Anti-namuna: hamma narsa uchun spy

Hamma narsa uchun spy — barcha testlarda mock o'rniga spy ishlatish xato. Spy yon ta'sirlarga ega bo'lishi mumkin bo'lgan real kodni bajaradi: faylga yozish, HTTP yuborish, global holatni o'zgartirish. Agar test qilinayotgan modul HTTP so'rov yuboradigan spy-obyektning metodini chaqirsa, test integratsion testga aylanadi, birlik test emas. Qoida: agar spy I/O operatsiyalari bo'lgan obyektni o'rab olsa — bu endi birlik test emas. I/O izolyatsiyasi uchun mock, faqat tashqi ta'sirlarsiz xotiradagi obyektlar uchun spy dan foydalaning.

Android-da Mockito.spy() va MockK-da spy

Mockito.spy() — Java/Kotlin loyihalarida ayg'oqchi yaratishning klassik usuli. spy() real obyektni qabul qiladi va o'ramni qaytaradi. Barcha chaqiruvlar sukut bo'yicha real obyektga yo'naltiriladi va natijalar qayd etiladi. Test bajarilgandan so'ng verify() orqali chaqiruvlar sonini va argumentlarni tekshirish mumkin. Test ma'lumotlarini qaytarishi kerak bo'lgan metodlar uchun doReturn/when ishlatiladi — bu „qisman stublash“ (partial mocking) deb ataladi.

kotlin
class AnalyticsReporterTest {

    private val realTracker = AnalyticsTracker()
    private val spyTracker = Mockito.spy(realTracker)

    fun test_event_tracked() {
        val event = AnalyticsEvent("login")
        spyTracker.track(event)

        Mockito.verify(spyTracker).track(event)
        assertEquals(1, spyTracker.getBufferedCount())
    }

    fun test_track_with_exception() {
        Mockito.doThrow(RuntimeException("network"))
            .when(spyTracker).flush()

        spyTracker.track(AnalyticsEvent("login"))
        assertTrue(spyTracker.hasPendingEvents())
    }
}

MockK.spyk() — korutinlar va sealed-sinflarni yaxshiroq qo'llab-quvvatlaydigan Kotlin loyihalari uchun muqobil. MockK.spyk() ayg'oqchi yaratadi, Mockito.spy()-ning analogi. Suspend-funksiyalar uchun coVerify va qisman stublash uchun every-ni qo'llab-quvvatlaydi. Mockito-dan farqli o'laroq, MockK final-sinflar uchun spy-ni qo'llab-quvvatlamaydi (Kotlin-dagi barcha sinflar sukut bo'yicha final) — sinfni ochish (open) yoki interfeysdan foydalanish kerak.

kotlin
class LoginUseCaseTest {

    private val realRepo = UserRepository()
    private val spyRepo = spyk(realRepo)

    private val useCase = LoginUseCase(spyRepo)

    fun test_login_calls_save() = runTest {
        every { spyRepo.getUser(any()) } returns User("test")

        val result = useCase.login("test", "pass")

        coVerify { spyRepo.saveLoginTime(any()) }
        assertTrue(result.isSuccess)
    }
}

Spy orqali qisman stublash

Qisman stublash — kuchli, ammo xavfli texnika. Siz obyektning spy-sini yaratib, faqat ba'zi metodlarni bekor qilishingiz (stub) mumkin, qolganlarini real qoldirishingiz mumkin. Misol: getUser() test ma'lumotlarini qaytaradigan, saveUser() esa haqiqatan ham xotiradagi ro'yxatga saqlaydigan spy-repozitoriy. Bu stublarning (boshqariladigan ma'lumotlar) va spy-ning (real mantiq) afzalliklarini birlashtirishga imkon beradi. Kamchilik: testni o'qish murakkabligi — qaysi metodlar real va qaysilari stub ekanligi aniq emas.

iOS-da OCMock va protokollar bilan Spy implementatsiyasi

Objective-C uchun OCMock — niceMock orqali spy-obyektlarini yaratishni qo'llab-quvvatlaydigan kutubxona. OCMock Objective-C ish vaqti muhitidan foydalanib metod chaqiruvlarini tutib oladi va qayd etadi. Test bajarilgandan so'ng verify chaqiriladi. OCMock har qanday obyekt uchun spy-ni qo'llab-quvvatlaydi (Objective-C da barcha metodlar dinamik), bu Swift dan ustunlik beradi, chunki Swift da spy faqat protokollar orqali mumkin.

objective-c
// Real obyekt uchun spy yaratish
AnalyticsTracker *realTracker = [[AnalyticsTracker alloc] init];
AnalyticsTracker *spy = [OCMockObject partialMockForObject:realTracker];

// Testni bajarish
[spy trackEvent:@"login"];

// Tekshirish
[[spy verify] trackEvent:@"login"];
XCTAssertEqual([realTracker eventCount], 1);

Swift protocol-based spy — Swift da Objective-C ning ish vaqti refleksiyasi yo'q, shuning uchun spy qo'lda yaratiladi. Test strukturasi protokolni implementatsiya qiladi va ichki tomondan real obyektni chaqiradi, bir vaqtning o'zida chaqiruvlarni qayd etadi. Bu ko'proq kod, lekin to'liq boshqariladigan va tip xavfsiz. Qo'lda ayg'oqchilar tashqi kutubxonalarni talab qilmaydi va ish vaqti muhitidan foydalanmaydi — hamma narsa kompilyatsiya bosqichida tekshiriladi.

swift
protocol AnalyticsProtocol {
    func trackEvent(name: String)
}

final class SpyAnalytics: AnalyticsProtocol {
    private let real: AnalyticsProtocol
    private var events: [String] = []

    init(real: AnalyticsProtocol) {
        self.real = real
    }

    func trackEvent(name: String) {
        events.append(name)
        real.trackEvent(name: name)
    }

    func verifyTracked(name: String) -> Bool {
        return events.contains(name)
    }
}

OCMock vs qo'lda spy qachon ishlatiladi — Objective-C kodi uchun OCMock dan foydalaning (kamroq boilerplate). Swift uchun — protokollar orqali qo'lda ayg'oqchilar afzal. Qo'lda spy chaqiruvlarni qayd etish ustidan to'liq nazorat beradi, refleksiyani talab qilmaydi va qiymat turlari (struct) bilan ishlaydi. Yagona kamchilik: yangi metodlar qo'shganda spy-sinf kodini protokol bilan sinxron saqlash kerak.

Spy-ni qo'llashning odatiy stsenariylari

Analitikani tekshirish — spy-dan foydalanishning eng keng tarqalgan stsenariysi. Ishlab chiqarish kodida analitika chaqiruvlari butun ilova bo'ylab tarqalgan: login, logout, purchase, error. Test AnalyticsTracker uchun spy-o'ram yaratadi, stsenariyni bajaradi (kirish, mahsulotni ko'rish, savatga qo'shish, sotib olish) va barcha kerakli hodisalarning to'g'ri tartibda yuborilganligini tekshiradi. Mock mos kelmaydi, chunki AnalyticsTracker buferlash va yuborish mantig'ini o'z ichiga oladi.

Timerlar va rejalashtiruvchilar — Handler (Android) yoki Timer (iOS) ishlatadigan kodni testlash haqiqiy vaqt tufayli qiyin. Scheduler uchun spy-o'ram qaysi vazifalar va qanday kechikish bilan rejalashtirilganligini qayd etadi. Test real Handler-ning spy-sini yaratadi, harakatni bajaradi va Handler.postDelayed(runnable, delay) to'g'ri kechikish bilan chaqirilganligini tekshiradi. Haqiqiy vazifa bu vaqtda bajarilmaydi — spy chaqiruvni tutib oladi va qayd etadi.

Loglash va debug ma'lumotlari — ishlab chiqarishda loglar o'chirilgan yoki faylga yozilgan bo'lishi mumkin. Logger uchun spy-o'ram barcha xabarlarni test bajarilgandan so'ng tekshiradigan xotiradagi ro'yxatga yozadi. Bu konsolni ifloslantirmasdan xato paytida to'g'ri xabar yozilganligini tekshirishga imkon beradi. Logger uchun qo'lda ayg'oqchilar, ayniqsa, iOS-da foydali, chunki OSLog test API-siga ega emas.

Chaqiruvlar tartibini tekshirish — ba'zi stsenariylar qat'iy operatsiyalar tartibini talab qiladi: ulanishni ochish, ma'lumot yuborish, ulanishni yopish. Mockito InOrder.verify() orqali tartibni tekshirishga imkon beradi. Spy ham xuddi shunday qiladi, lekin real bajarilishni saqlaydi. Agar nafaqat tartib, balki har bir qadamning natijasi ham muhim bo'lsa (ulanish haqiqatan ochildi) — mock emas, spy dan foydalaning.

Tez-tez beriladigan savollar

Spy vs Mock: asosiy farq nima?

Spy real obyektni o'rab oladi va uning mantig'ini bajaradi, qo'shimcha ravishda chaqiruvlarni qayd etadi. Mock obyektni to'liq qaralama bilan almashtiradi — hech qanday real mantiq bajarilmaydi. Spy xatti-harakatni saqlaydi, mock esa saqlamaydi. Obyektning real ishi muhim bo'lganda spy-ni tanlang; testni tashqi bog'liqlikdan izolyatsiya qilish kerak bo'lganda mock-ni tanlang.

Spy qachon yomon tanlov?

Spy-o'ram real I/O operatsiyalariga olib kelganda. Agar spy faylga yozadigan, HTTP yuboradigan yoki diskdan o'qiydigan obyektni o'rab olsa — test birlik test bo'lishdan to'xtaydi. Ikkinchi holat: test faqat qaytarilgan qiymatni chaqiruvlarga qiziqmasdan tekshiradi — bu erda stub etarli, spy ortiqcha. Uchinchi: kod spy-ning ichki holatiga tayanadi — bu mo'rt test.

MockK spy-ni qo'llab-quvvatlaydimi?

Ha, spyk() orqali — Mockito.spy()-ning analogi. MockK.spyk() real obyekt atrofida ayg'oqchi yaratadi, qisman stublash uchun every va suspend-funksiyalar uchun coVerify/coroutinesVerify-ni qo'llab-quvvatlaydi. Cheklov: final-sinflar bilan ishlamaydi (open yoki interfeys kerak). Java sinflari uchun MockK ham spyk()-ni qo'llab-quvvatlaydi, lekin @MockKJvmInline annotatsiyasini talab qiladi.

Mock-dan Spy yasash mumkinmi?

Texnik jihatdan — yo'q. Mock bu real implementatsiyaga ega bo'lmagan qaralama. Spy ta'rifiga ko'ra real obyektni o'rab oladi. Mockito-da mock-ni spy-ga aylantirib bo'lmaydi. Ammo aksini qilish mumkin: spy yaratib, doReturn/when (partial mocking) orqali metodlarning bir qismini bekor qilish. Bu spy-obyektning tanlangan metodlari uchun mock-ga o'xshash xatti-harakat beradi.

Swift-da Spy albatta protokol orqalimi?

Albatta. Swift-da Java/Kotlin-dagi kabi dinamik proksilash yo'q. Spy yaratish uchun ham ishlab chiqarish sinfi, ham spy sinfi tomonidan implementatsiya qilinadigan protokol kerak. Swift-protocol-based spy — real obyektni qabul qiladigan, unga chaqiruvlarni yo'naltiradigan va metama'lumotlarni qayd etadigan qo'lda implementatsiya. Muqobil: Cuckoo kutubxonasi, SourceKit orqali spy-sinflarini yaratadi.

Xulosa

  • Spy — mantiqni almashtirmasdan barcha metod chaqiruvlarini qayd etuvchi real obyekt atrofidagi o'ram
  • Mock-dan farq — spy real kodni bajaradi, mock uni qaralama bilan almashtiradi
  • Mockito.spy() — Java/Kotlin uchun, verify va partial mock imkoniyati bilan real obyektni o'rab oladi
  • MockK.spyk() — korutinlar, sealed-sinflar va coVerify qo'llab-quvvatlashi bilan Kotlin analogi
  • iOS — Objective-C uchun OCMock, Swift uchun qo'lda protocol-based spy
  • Asosiy stsenariy — analitika, timerlar, loglar va chaqiruvlar tartibini tekshirish
  • Diqqat — I/O operatsiyalari bo'lgan spy birlik testni integratsion testga aylantiradi

Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz

IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.

Loyihani muhokama qilish

Shuningdek o'qing