Test-Driven Development (TDD) — bu kodni amalga oshirishdan oldin testlar yoziladigan rivojlanish metodologiyasidir. Dasturchi avval kutilgan xatti-harakatni muvaffaqiyatsiz test shaklida shakllantiradi, so'ng uni o'tish uchun minimal kod yozadi va bundan keyin natijani refaktor qiladi. Martin Fowler (2023) ga ko'ra, TDD test texnikasi emas — bu arxitekturani intizomga soladigan va kod yozish bosqichida nuqsonlar sonini kamaytiradigan dizayn texnikasidir.
Asosiy
Test-Driven Development — bu avtomatlashtirilgan testlar ishlab chiqarish kodini yozishni belgilaydigan dasturiy ta'minotni ishlab chiqish amaliyotidir. An'anaviy yondashuvdan farqli o'laroq, kod avval yoziladi, keyin test qilinadi, TDD ketma-ketlikni teskari aylantiradi: avval test yoziladi, so'ng bu testni o'tadigan kod.
TDD asoschisi Kent Beck hisoblanadi, u bu amaliyotni 1990-yillarning oxirida Extreme Programming (XP) metodologiyasi doirasida shakllantirgan. “Test-Driven Development: By Example” (2002) kitobida Bek TDD ning beshta qoidasini tasvirlagan: ishlab chiqarish kodidan oldin test yoz, testni o'tish uchun qancha kod kerak bo'lsa, shuncha yoz va har bir sikldan keyin refaktor qil.
Birinchi tamoyil — test interfeysni belgilaydi. Dasturchi komponent qanday amalga oshirilganligi haqida o'ylashdan oldin undan qanday foydalanish haqida o'ylashga majbur bo'ladi. Bu boshidanoq toza API ni shakllantiradi.
Ikkinchi tamoyil — minimal amalga oshirish. Test yozilganda, dasturchi uni o'tish uchun qancha ishlab chiqarish kodi kerak bo'lsa, shuncha yozadi — bir satr ortiq emas. Bu Martin Fowler Speculative Generality deb ataydigan muddatidan oldin abstraksiya va haddan tashqari murakkablikning oldini oladi.
TDD va “faktdan keyin” test o'rtasidagi asosiy farq — ketma-ketlik intizomi. TDD da test nafaqat kodni tekshiradi — balki uning tuzilishini yo'naltiradi. Microsoft Research (Nagappan et al., 2008) tadqiqotiga ko'ra, TDD ni qo'llaydigan jamoalar an'anaviy yondashuvni qo'llaydigan jamoalarga nisbatan nuqson zichligini 40–90% ga kamaytiradi.
Red-Green-Refactor sikli — har bir yangi test uchun takrorlanadigan uch bosqichli ketma-ketlikdir. Red: o'tmaydigan test yoz. Green: testni o'tish uchun minimal kod yoz. Refactor: xatti-harakatini o'zgartirmasdan kodni yaxshila.
Dasturchi hali amalga oshirilmagan funksionallikni tekshiradigan test yozadi. Bu bosqichda test muvaffaqiyatsiz bo'lishi kerak — bu test haqiqatan ham nimanidir tekshirayotganini tasdiqlaydi. Android ishlab chiqish muhitida JUnit 5 freymvorki muvaffaqiyatsiz testlar uchun qizil indikatsiyani ko'rsatadi, bu bosqichga nom bergan.
class CalculatorTest {
fun testAddition() {
val result = Calculator().add(2, 3)
Assertions.assertEquals(5, result)
}
}
Bu bosqichda testni o'tish uchun yetarli minimal ishlab chiqarish kodi yoziladi. Hech qanday ortiqchalik — faqat yashil indikatsiya uchun kerakli narsa. Agar amalga oshirish doimiy bo'lishi mumkin bo'lsa — doimiy bo'lsin. Refaktoring yangi testlar paydo bo'lganda keyingi bosqichda sodir bo'ladi.
class Calculator {
fun add(a: Int, b: Int): Int {
return a + b
}
}
Yashil test refaktoring uchun sug'urtadir. Dasturchi amalga oshirishni qayta yozishi, samaradorlikni optimallashtirishi yoki o'qiluvchanlikni yaxshilashi mumkin, test kutilgan xatti-harakatdan har qanday chetlanishni darhol aniqlashiga ishonch hosil qilgan holda. Android da mobil ishlanmada bu bosqich umumiy interfeyslarni ajratish va kod takrorlanishini kamaytirish uchun ayniqsa muhimdir.
TDD ni mobil loyihalarda qo'llash ham akademik tadqiqotlar, ham yetakchi ishlab chiqish studiyalarining amaliyoti bilan tasdiqlangan o'lchanadigan afzalliklarni beradi.
IBM (Bhat & Nagappan, 2006) tomonidan to'rtta sanoat loyihasida o'tkazilgan tadqiqot shuni ko'rsatdiki, TDD dan foydalanadigan jamoalar an'anaviy sxema bo'yicha ishlaydigan o'xshash jamoalarga nisbatan 40% kam nuqson chiqaradi. Google Play da chiqarilgandan keyin xatoni tuzatish narxi kod yozish bosqichiga qaraganda ancha yuqori bo'lgan mobil ishlanma uchun bu metrik muhimdir.
TDD bo'yicha yozilgan testlar API ning jonli hujjati sifatida xizmat qiladi. Loyihaga kelgan dasturchi testlarni o'qib, har bir komponentdan qanday foydalanish kerakligini tushunishi mumkin. Bu ayniqsa mobil studiyalarning odatiy muammosi bo'lgan yuqori kadrlar almashinuvi sharoitida qimmatlidir.
90% dan ortiq kod qamrovi dasturchilarga biror narsani buzish qo'rquvisiz refaktoring o'tkazish imkonini beradi. Google o'zining “Software Engineering at Google” (2020) kitobida test qamrovini millionlab satr kodli loyihalarda kod bazasini toza saqlashga imkon beradigan asosiy omil deb ataydi.
Mobil ishlanmada TDD ekotizimiga birlik testlari, mocking va UI komponentlarini tekshirish vositalari kiradi — Android va iOS uchun.
| Vosita | Platforma | Maqsad |
|---|---|---|
| JUnit 5 | Android (Kotlin/Java) | Birlik testlari uchun asosiy freymvork |
| Mockito | Android | Mock ob'ektlarini yaratish va chaqiruvlarni tekshirish |
| MockK | Android (Kotlin) | Kotlin-first sintaksisi va korutinlarni qo'llab-quvvatlash bilan mocking |
| Turbine | Android | Kotlin Flow va reaktiv oqimlarni test qilish |
| XCTest | iOS (Swift) | Standart test freymvorki |
Kotlin da Android loyihalari uchun standart to'plam JUnit 5 + MockK ni o'z ichiga oladi. MockK Mockito dan afzal ko'riladi, chunki u qo'shimcha sozlamalarsiz Kotlin birinchi darajali funksiyalarini — sealed class, korutinlar va suspend-funksiyalarni qo'llab-quvvatlaydi.
iOS ishlanmasida TDD XCTest orqali amalga oshiriladi — Apple ning o'rnatilgan freymvorki, asertsiyalar, test sinflari va CI/CD bilan Xcode Server yoki GitHub Actions orqali integratsiyani ta'minlaydi. iOS da mocking uchun Cuckoo va OHHTTPStubs kutubxonalari ishlatiladi.
Android uchun Kotlin tilida real TDD stsenariysini ko'rib chiqaylik — foydalanuvchi repozitoriyasini test qilish. Avval test yozamiz, so'ng — bu testni o'tadigan amalga oshirish.
class UserRepositoryTest {
private val api = mockk<UserApi>()
private val dao = mockk<UserDao>()
private val repo = UserRepository(api, dao)
fun `when api returns user then cache and emit`() = runTest {
val user = User(1, "Alice")
coEvery { api.getUser(1) } returns user
every { dao.insert(user) } returns Unit
val result = repo.getUser(1)
assertEquals(user, result)
verify { dao.insert(user) }
}
}
class UserRepository(
private val api: UserApi,
private val dao: UserDao
) {
suspend fun getUser(id: Int): User {
val user = api.getUser(id)
dao.insert(user)
return user
}
}
Birinchi testni o'tgandan so'ng ikkinchisini qo'shamiz — tarmoq xatosi da xatti-harakatni tekshiramiz. Endi test belgilaydiki, API muvaffaqiyatsiz bo'lganda repozitoriy keshdan ma'lumotlarni qaytarishi kerak.
fun `when api fails then return cached user`() = runTest {
val cached = User(1, "Cached Alice")
coEvery { api.getUser(1) } throws IOException()
every { dao.getById(1) } returns cached
val result = repo.getUser(1)
assertEquals(cached, result)
}
TDD ga o'tish metodologiyaning barcha afzalliklarini yo'qqa chiqarishi mumkin bo'lgan odatiy xatolar bilan birga keladi. Bu tuzoqlarni tushunish jamoalarga amaliyotni samaraliroq joriy etishga yordam beradi.
Birinchi va eng keng tarqalgan anti-namuna — bitta testda juda katta hajmdagi funksionallikni test qilish. Test aynan bitta tasdiqni (bitta asertsiyani) tekshirishi kerak. Test muvaffaqiyatsiz bo'lsa, dasturchi qo'shimcha disk raskadrovkasiz nima buzilganini bilishi kerak.
Ikkinchi xato — boshidanoq o'tadigan test yozish. Test hech bo'lmaganda bir marta qizil bo'lmagan bo'lsa, uning umuman nimanidir tekshirayotganiga ishonch yo'q. Qoida: muvaffaqiyatsiz bo'lganini ko'rmagan testingga hech qachon ishonma.
Uchinchi odatiy xato — yashil bosqichda to'xtash. Refaktoring siklning ixtiyoriy emas, balki majburiy bosqichidir. Usiz kod bazasi degradatsiyaga uchraydi, testlar mo'rt bo'ladi va TDD ning afzalliklari yo'qoladi.
Tez-tez beriladigan savollar
TDD birinchi navbatda dizayn texnikasidir, test texnikasi emas. TDD da testlar spetsifikatsiya rolini o'ynaydi: ular komponentning API sini uni amalga oshirishdan oldin belgilaydi. Kent Beck ning o'zi TDD ni “dizayn intizomi, test intizomi emas” deb ataydi.
Microsoft Research tadqiqotlariga ko'ra, TDD odatga aylanishi uchun jamoalarga 3 oydan 6 oygacha uzluksiz amaliyot kerak. Dastlabki 2–3 hafta mahsuldorlik 15–30% ga tushadi, ammo moslashishdan keyin disk raskadrovka vaqtining qisqarishi hisobiga dastlabki darajaga qaytadi yoki undan oshadi.
Ha, lekin cheklovlar bilan. UI mantiqi (ViewModel, State) uchun TDD bevosita qo'llaniladi. Vizual komponentlar (Compose UI, SwiftUI Views) uchun ekran tasviri testi (snapshot testing) TDD ni to'ldiradi, lekin uni almashtirmaydi. Biznes mantiqini va taqdimotni ajratish tavsiya etiladi.
Legacy kod uchun “xarakterizatsiya testlari” (characterization tests) strategiyasi tavsiya etiladi — testlar mavjud xatti-harakatga yoziladi, so'ng kod refaktor qilinadi. Michael Feathers ning “Working Effectively with Legacy Code” (2004) kitobida tasvirlangan bu yondashuv TDD ni bosqichma-bosqich joriy etishga imkon beradi.
TDD va Clean Architecture bir-birini kuchaytiradi. Toza arxitektura qatlamlar o'rtasida aniq chegaralarni talab qiladi, TDD esa dasturchini bu chegaralarni testlar orqali loyihalashga majbur qiladi. Domen qatlami mock-bog'liqliklar bilan izolyatsiya qilingan holda test qilinadi, ma'lumot qatlami — integratsiya testlari orqali.
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.