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 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 — 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.
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.
| Stsenariy | Spy | Mock |
|---|---|---|
| Real mantiq kerak | Ha | Yo'q (qaralama) |
| Chaqiruvlarni tekshirish | Ha (son, argumentlar) | Ha (son, argumentlar) |
| Qisman stublash | Ha (ba'zi metodlar — spy, boshqalari — stub) | Yo'q (barcha metodlar — qaralama) |
| Yon ta'sirlar xavfi | Yuqori (real kod) | Nol |
| Tezlik | Pastroq (real mantiq) | Yuqoriroq (qaralamalar) |
| O'qilishlik | Pastroq (nimaning real ekanligini tushunish qiyin) | Yuqoriroq (hamma narsa aniq) |
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.
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.
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.
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)
}
}
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.
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.
// 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.
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.
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 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-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.
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.
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.
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
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.