Test Doubles — bunlar real asılılıqların əvəzinə vahid testlərdə istifadə olunan əvəzedici obyektlərdir. Termin Gerard Meszaros tərəfindən „xUnit Test Patterns“ (2007) kitabında Mock, Stub, Fake, Spy və Dummy üçün ümumiləşdirici anlayış kimi təqdim edilmişdir. Martin Fowler (2024)-ə görə, Test Doubles test edilən komponenti onun mühitindən təcrid etməyə imkan verir, testləri deterministik, sürətli və xarici xidmətlərdən asılı olmayan edir.
Başlıca
Test Doubles — bu avtomobil sənayesindən (kaskadyor, aktyorlar üçün „dubl“) proqram təminatının hazırlanmasına köçürülmüş termindir. Kaskadyor aktyoru təhlükəli səhnədə əvəz etdiyi kimi, Test Double test ssenarisində real komponenti əvəz edir. Bu, real asılılıq əlçatmaz, yavaş, qeyri-deterministik və ya yan təsirlərə malik olduqda zəruridir.
Test Double anlayışı beş konkret növü əhatə edir, hər biri öz vəzifəsini həll edir. Meszaros tipologiyası kanonikdir və bütün müasir test təlimatlarında istifadə olunur. Növlər arasındakı fərq nəzarət və yoxlama dərəcəsindədir: sadə parametr doldurmadan (Dummy) tutmuş çağırış ardıcıllığının tam yoxlanmasına qədər (Mock).
Test Doubles-in əsas məqsədi test edilən modulun təcrid edilməsidir. Mobil proqramlaşdırmada real asılılıqlar API serverləri, verilənlər bazaları, fayl sistemi, cihaz sensorları, sistem xidmətləridir (LocationManager, Camera, Bluetooth). Bu komponentlərin birbaşa istifadəsi testləri yavaş, kövrək və mühitdən asılı edir. Google Testing Blog (2023) məlumatlarına görə, yaxşı təcrid edilmiş vahid testlər millisaniyələrdə, inteqrasiya testləri isə saniyə və dəqiqələrdə yerinə yetirilir.
Gerard Meszaros təsnifatı davranış və istifadə məqsədinə görə fərqlənən beş növ Test Doubles-ı əhatə edir. Onlar arasındakı fərqi başa düşmək düzgün vahid test etmənin əsasıdır.
Dummy — bu test edilən metoda ötürülən, lakin heç vaxt istifadə olunmayan obyektdir. Dummy yalnız metodun imzasını təmin etmək üçün lazımdır. Kotlin-də bu çox vaxt null, emptyList() və ya əvəzediciləri olan obyektdir. Dummy heç bir məntiq ehtiva etməməlidir — əgər çağırılarsa, test uğsuz olmalıdır.
Fake — bu interfeysin sadələşdirilmiş, lakin işləyən tətbiqidir. Mock və Stub-dan fərqli olaraq, Fake real biznes məntiqini ehtiva edir, lakin sadələşdirilmiş formada. Klassik nümunə — verilənləri verilənlər bazası əvəzinə HashMap-də saxlayan InMemoryUserRepository. Fake, vəziyyətdən asılı məntiqi real infrastrukturun yükü olmadan test etmək lazım olduqda istifadə olunur.
| Tip | Təyinat | Nümunə |
|---|---|---|
| Dummy | Parametri doldurmaq | null, boş obyekt |
| Fake | İşləyən sadələşdirilmiş tətbiq | InMemoryRepository |
| Stub | Sabit dəyəri qaytarmaq | when(api.getUser()).thenReturn(user) |
| Spy | Yoxlama üçün çağırışları qeyd etmək | verify(spy).save(user) |
| Mock | Qarşılıqlı əlaqəni yoxlamaq | verify(mock).sendEmail(email) |
Stub müəyyən çağırışlar üçün əvvəlcədən müəyyən edilmiş dəyərləri qaytarır. Stub çağırılıb-çağırılmadığını yoxlamır — sadəcə məlumat təqdim edir. Mockito-da Stub when(method).thenReturn(value) vasitəsilə yaradılır. Stub, asılılığın konkret dəyər qaytarması lazım olduqda, lakin çağırış faktının önəmli olmadığı halda test etmək üçün idealdır.
Spy — bu real obyektin ətrafında sonrakı yoxlama üçün bütün çağırışları qeyd eden sarğıcıdır. Mock-dan fərqli olaraq, Spy çağırışları real obyektə ötürür, lakin onların baş verdiyini yoxlamağa imkan verir. Mockito-da Spy spy(realObject) vasitəsilə yaradılır. Spy, real obyektdən istifadə etmək istəyən, lakin bəzi çağırışları yoxlamaq istəyən hallarda qismən mocking üçün faydalıdır.
Mock — bu əvvəlcədən müəyyən edilmiş çağırış gözləntiləri olan obyektdir. Mock müəyyən metodların müəyyən arqumentlərlə və müəyyən ardıcıllıqla çağırılıb-çağırılmadığını yoxlayır. Stub-dan fərqli olaraq, Mock məlumat qaytarmağa deyil, davranışın yoxlanmasına diqqət yetirir. Mock mobil proqramlaşdırmada ən güclü və ən çox istifadə olunan Test Double növüdür.
Mock və Stub arasındakı fərq hətta təcrübəli proqramçılar arasında da çox vaxt qarışıqlığa səbəb olur. Əsas fərq məqsəddədir: Stub vəziyyəti yoxlayır (state verification), Mock davranışı yoxlayır (behavior verification).
Stub suala cavab verir: „kod düzgün nəticə qaytardımı?“. Mock suala cavab verir: „kod düzgün metodları düzgün arqumentlərlə çağırdımı?“. Mobil proqramlaşdırmada Stub nəticə vacib olduqda (məsələn, repozitoridən məlumatlar), Mock isə yan təsirlər vacib olduqda (məsələn, e-poçt göndərmək, verilənlər bazasına yazmaq) istifadə olunur.
// Stub: vəziyyətin yoxlanılması
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)
// Mock: davranışın yoxlanılması
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }
Android layihələri üçün ən məşhur mocking kitabxanası olan MockK istifadə edərək Kotlin-də bütün beş növ Test Doubles-un praktik nümunələri.
class InMemoryUserRepository : UserRepository {
private val store = mutableMapOf<String, User>()
override fun save(user: User) {
store[user.email] = user
}
override fun findByEmail(email: String): User? {
return store[email]
}
}
class RegisterUseCaseTest {
private val api = mockk<AuthApi>()
private val repo = spyk(InMemoryUserRepository())
private val useCase = RegisterUseCase(api, repo)
fun `register user successfully`() = runTest {
// Stub: sabit API cavabını qaytarırıq
coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")
val result = useCase.execute("test@test.com")
// Verify: istifadəçinin yadda saxlanıldığını yoxlayırıq
verify { repo.save(any()) }
assertTrue(result.isSuccess())
}
}
data class Logger(val appContext: Context, val format: FormatType)
fun `test logger with dummy context`() {
// Dummy: Context Logger daxilində istifadə olunmur
val dummyContext = mockk<Context>()
val logger = Logger(dummyContext, FormatType.JSON)
assertEquals(FormatType.JSON, logger.format)
}
Test Double növünün seçimi dəqiq nəyin test edildiyindən asılıdır: vəziyyət, davranış və ya inteqrasiya. Android və iOS üzrə mobil proqramlaşdırmada aşağıdakı tövsiyələr formalaşmışdır.
ViewModel-in test edilməsində yan təsirlər yaradan asılılıqlar (repozitorilər, analitika, naviqasiya) üçün Mock, məlumat qaytaran asılılıqlar (API müştəriləri, ContentProvider) üçün isə Stub istifadə edin. Bu, ViewModel-in həm uğurlu, həm də səhv ssenarilərini düzgün idarə etdiyini yoxlamağa imkan verir.
Repository səviyyəsində Fake (yaddaşdaxili verilənlər bazası tətbiqləri) və Stub (sabit API cavabları) üstünlük təşkil edir. Fake SQLite konfiqurasiyası olmadan keşləmə və oflayn rejim məntiqini yoxlamağa imkan verir. Stub müxtəlif HTTP statuslarını simulyasiya edir: 200, 404, 500, timeout.
Test Doubles-in düzgün istifadə edilməməsi hər refaktorinqdə pozulan kövrək testlərin ən çox yayılmış səbəblərindən biridir.
Ən çox yayılmış səhv — hər şeyin mock edilməsidir. Əgər testdə hər asılılıq Mock ilə əvəz edilirsə, test real davranışı yoxlamağı dayandırır. Mock yalnız xarici asılılıqlar üçün olmalıdır (şəbəkə, verilənlər bazası, fayl sistemi, sistem xidmətləri). Tətbiqin daxili komponentləri (Value Object, data class, sadə köməkçi alətlər) əvəz edilməməlidir.
İkinci səhv — gözləntilər müəyyən edilmədən Mock yaratmaq. Əgər metod every / when olmadan çağırılırsa, Mock standart dəyər qaytarır (null, 0, false). Bu, Mock səssizcə null qaytardıqda və test bunu düzgün davranış kimi şərh etdikdə yalançı müsbət testlərə səbəb ola bilər.
Üçüncü səhv — hər Mock-un hər çağırışının yoxlanmasıdır. Verify yalnız biznes məntiqi baxımından kritik olan çağırışlar üçün istifadə edilməlidir. Həddən artıq yoxlama testləri kövrək edir: istehsal kodunda çağırış ardıcıllığının dəyişməsi davranışı dəyişmədən testləri pozur.
Tez-tez verilən suallar
Stub məlumatları qaytarır və vəziyyəti yoxlayır (nə qaytarıldı), Mock isə davranışı yoxlayır (hansı metodların çağırıldığı). Stub = „X-i qaytar“, Mock = „Y-nin Z arqumenti ilə çağırıldığını yoxla“. Real testlərdə bir obyekt çox vaxt həm Stub, həm də Mock rolunu oynayır.
Fake Mock-dan üstün tutulur, nə zaman ki vəziyyətdən asılı məntiq test edilir: keşləmə, oflayn rejim, tranzaksiyalar. Fake (yaddaşdaxili tətbiq) kövrək verify çağırışları olmadan bu ssenariləri yoxlamağa imkan verir. Mock məlumat göndərilməsinin yoxlanması üçün daha uyğundur: analitika, push bildirişləri, e-poçt.
Kotlin-də Android layihələri üçün MockK tövsiyə olunur. O, korutinləri, suspend-funksiyaları, sealed class və extension-funksiyaları əlavə konfiqurasiya olmadan dəstəkləyir. Java layihələri üçün standart hələ də Mockito olaraq qalır — geniş sənədləşdirməyə malik ən məşhur kitabxana.
Kotlin Flow-un test edilməsi üçün MockK ilə birlikdə Turbine kitabxanasından istifadə edin. Turbine Flow emissiyasının yoxlanmasını sadələşdirir: dəyərlərin sırasını, axının tamamlanmasını və istisnaları yoxlamaq olar. Flow üçün Stub flowOf(value) qaytarır, Mock Flow-un yığıldığını yoxlayır.
Bəli, lakin API cavabları səviyyəsində, UI komponentləri yox. MockWebServer (OkHttp) və WireMock kitabxanaları UI testlərində HTTP cavablarını əvəz etməyə imkan verir. UI komponentlərinin özləri (Compose, SwiftUI Views) əvəz edilməməlidir — onların davranışı screenshot testləri və Espresso vasitəsilə test edilir.
Nəticələr
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.
Həm də oxuyun