10-bo‘lim
TDD - avval test, keyin kod
Qizil-yashil-refaktor tsikli to'liq amaliy misolda, TDD ning uch qoidasi, u nima beradi va qachon to'g'ri kelmaydi.
Ushbu bo‘lim mundarijasi
Shu paytgacha biz avval kod yozib, keyin unga test yozdik. TDD (Test-Driven Development) tartibni teskari qiladi: avval test, keyin kod.
Bu g'alati tuyuladi - hali yo'q narsani qanday tekshirish mumkin? Aynan shu g'alatilik TDD ning butun kuchi.
Qizil-yashil-refaktor #
Endi buni amalda bajaramiz. Vazifa - kutubxonada kitob kechiktirilganda jarima hisoblash. Qoidalar:
| Kechikish | Jarima |
|---|---|
| 0 kun | Yo'q |
| 1-7 kun | Har kunga 1000 so'm |
| 8-kundan boshlab | Birinchi 7 kun uchun 7000, keyin har kunga 2000 |
| Juda uzoq | Jami 50 000 so'mdan oshmaydi |
Bu qoidalarni bir yo'la kodga aylantirish mumkin edi. Biz esa bittalab qo'shamiz.
Tsikl 1: kechikish yo'q #
Qizil - test yozamiz. Kod hali yo'q:
from jarima import jarima_hisobla
def test_kechikish_yoq_bolsa_jarima_yoq():
assert jarima_hisobla(0) == 0
pytest -q --tb=line test_jarima.py
================================ ERRORS ================================
___________________ ERROR collecting test_jarima.py ____________________
ImportError while importing test module '/home/dasturchi/loyiha/test_jarima.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/usr/lib/python3.13/importlib/__init__.py:88: in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
test_jarima.py:1: in <module>
from jarima import jarima_hisobla
E ModuleNotFoundError: No module named 'jarima'
======================= short test summary info ========================
ERROR test_jarima.py
!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!
1 error in 0.07s
Bu ham qizil bosqich - modul topilmadi. Endi eng kam kod yozamiz:
def jarima_hisobla(kechikkan_kunlar):
return 0
pytest -q test_jarima.py
. [100%]
1 passed in 0.00s
Yashil. Funksiya doim 0 qaytaradi - bu "aldov" ko'rinadi, lekin TDD da bu to'g'ri qadam: hozircha bizda undan ko'proq talab qiladigan test yo'q.
Tsikl 2: kuniga 1000 so'm #
Qizil:
from jarima import jarima_hisobla
def test_kechikish_yoq_bolsa_jarima_yoq():
assert jarima_hisobla(0) == 0
def test_bir_kun_kechikish_ming_som():
assert jarima_hisobla(1) == 1000
def test_uch_kun_kechikish():
assert jarima_hisobla(3) == 3000
pytest -q --tb=line test_jarima.py
.FF [100%]
=============================== FAILURES ===============================
E assert 0 == 1000
+ where 0 = jarima_hisobla(1)
/home/dasturchi/loyiha/test_jarima.py:9: assert 0 == 1000
E assert 0 == 3000
+ where 0 = jarima_hisobla(3)
/home/dasturchi/loyiha/test_jarima.py:13: assert 0 == 3000
======================= short test summary info ========================
FAILED test_jarima.py::test_bir_kun_kechikish_ming_som - assert 0 == ...
FAILED test_jarima.py::test_uch_kun_kechikish - assert 0 == 3000
2 failed, 1 passed in 0.01s
Yashil - endi 0 qaytarish yetarli emas:
def jarima_hisobla(kechikkan_kunlar):
return kechikkan_kunlar * 1000
pytest -q test_jarima.py
... [100%]
3 passed in 0.01s
Uchta test o'tdi. E'tibor bering: kechikkan_kunlar == 0 holati ham
o'z-o'zidan to'g'ri ishladi.
Tsikl 3: sakkizinchi kundan qimmatroq #
Qizil:
from jarima import jarima_hisobla
def test_kechikish_yoq_bolsa_jarima_yoq():
assert jarima_hisobla(0) == 0
def test_bir_kun_kechikish_ming_som():
assert jarima_hisobla(1) == 1000
def test_uch_kun_kechikish():
assert jarima_hisobla(3) == 3000
def test_yettinchi_kun_hali_arzon():
assert jarima_hisobla(7) == 7000
def test_sakkizinchi_kundan_ikki_barobar():
assert jarima_hisobla(8) == 9000
def test_on_kun_kechikish():
assert jarima_hisobla(10) == 13000
pytest -q --tb=line test_jarima.py
....FF [100%]
=============================== FAILURES ===============================
E assert 8000 == 9000
+ where 8000 = jarima_hisobla(8)
/home/dasturchi/loyiha/test_jarima.py:21: assert 8000 == 9000
E assert 10000 == 13000
+ where 10000 = jarima_hisobla(10)
/home/dasturchi/loyiha/test_jarima.py:25: assert 10000 == 13000
======================= short test summary info ========================
FAILED test_jarima.py::test_sakkizinchi_kundan_ikki_barobar - assert ...
FAILED test_jarima.py::test_on_kun_kechikish - assert 10000 == 13000
2 failed, 4 passed in 0.01s
Ikkita yangi test yiqildi, eskilar yashil qoldi - aynan shuni kutgandik.
Yashil:
def jarima_hisobla(kechikkan_kunlar):
if kechikkan_kunlar <= 7:
return kechikkan_kunlar * 1000
return 7000 + (kechikkan_kunlar - 7) * 2000
pytest -q test_jarima.py
...... [100%]
6 passed in 0.01s
Tsikl 4: yuqori chegara #
Qizil:
from jarima import jarima_hisobla
def test_kechikish_yoq_bolsa_jarima_yoq():
assert jarima_hisobla(0) == 0
def test_bir_kun_kechikish_ming_som():
assert jarima_hisobla(1) == 1000
def test_uch_kun_kechikish():
assert jarima_hisobla(3) == 3000
def test_yettinchi_kun_hali_arzon():
assert jarima_hisobla(7) == 7000
def test_sakkizinchi_kundan_ikki_barobar():
assert jarima_hisobla(8) == 9000
def test_on_kun_kechikish():
assert jarima_hisobla(10) == 13000
def test_yigirma_sakkiz_kun_chegaraga_yetmaydi():
assert jarima_hisobla(28) == 49000
def test_yuz_kun_kechikish_chegarada_toxtaydi():
assert jarima_hisobla(100) == 50000
pytest -q --tb=line test_jarima.py
.......F [100%]
=============================== FAILURES ===============================
E assert 193000 == 50000
+ where 193000 = jarima_hisobla(100)
/home/dasturchi/loyiha/test_jarima.py:33: assert 193000 == 50000
======================= short test summary info ========================
FAILED test_jarima.py::test_yuz_kun_kechikish_chegarada_toxtaydi - as...
1 failed, 7 passed in 0.01s
Yashil:
def jarima_hisobla(kechikkan_kunlar):
if kechikkan_kunlar <= 7:
return kechikkan_kunlar * 1000
return min(50_000, 7000 + (kechikkan_kunlar - 7) * 2000)
pytest -q test_jarima.py
........ [100%]
8 passed in 0.01s
Refaktor #
Sakkizta test yashil. Endi - va faqat endi - kodni tozalaymiz. Sehrli sonlarni nomlangan doimiylarga chiqaramiz va manfiy kunni ham qaraymiz:
ARZON_KUNLAR = 7
ARZON_NARX = 1000
QIMMAT_NARX = 2000
ENG_KOP_JARIMA = 50_000
def jarima_hisobla(kechikkan_kunlar):
"""Kechikkan kunlar soniga qarab jarima summasini qaytaradi."""
if kechikkan_kunlar <= 0:
return 0
if kechikkan_kunlar <= ARZON_KUNLAR:
return kechikkan_kunlar * ARZON_NARX
qimmat_kunlar = kechikkan_kunlar - ARZON_KUNLAR
jami = ARZON_KUNLAR * ARZON_NARX + qimmat_kunlar * QIMMAT_NARX
return min(ENG_KOP_JARIMA, jami)
pytest -q test_jarima.py
........ [100%]
8 passed in 0.01s
Refaktoring paytida birorta test o'zgarmadi. Shuning uchun biz o'zgarish hech narsani buzmaganiga ishonch hosil qildik - bu TDD ning eng katta amaliy foydasi.
Robert Martin TDD ni uchta qoidada ifodalagan:
- Yiqiladigan test bo'lmasa, ishlab chiqarish kodini yozmang.
- Testdan faqat yiqilishi uchun yetarlicha yozing (kompilyatsiya xatosi ham yiqilish hisoblanadi).
- Yiqilgan testni o'tkazish uchun yetarlicha kod yozing - undan ortiq emas.
Uchinchi qoida eng qiyini. "Baribir kerak bo'ladi" degan fikr bilan oldindan kod yozish - TDD dan chiqib ketishning eng keng tarqalgan yo'li.
TDD nima beradi #
| Foyda | Qanday paydo bo'ladi |
|---|---|
| To'liq qoplama | Kodning har qatori test tufayli yozilgan |
| Oddiy dizayn | Ortiqcha narsa yozilmaydi - talab qiluvchi test yo'q |
| Testlanadigan kod | Test birinchi bo'lgani uchun kod unga moslashadi |
| Erta qaytish aloqasi | Xato yozilgandan keyin daqiqalar ichida ko'rinadi |
| Qo'rquvsiz refaktoring | Tarmoq mavjud - o'zgartirib ko'rish mumkin |
| Jonli hujjat | Testlar - talablarning bajariladigan ro'yxati |
Ikkinchi qator ko'pincha e'tibordan chetda qoladi. TDD da siz
jarima_hisobla ga "keyin kerak bo'ladi" degan parametrlarni
qo'shmaysiz - chunki ularni talab qiladigan test yo'q. Kod tabiiy
ravishda kichik qoladi.
TDD qachon to'g'ri kelmaydi #
"Har qator test tufayli yozilgan" degani - har xatti-harakat tekshirilgan degani emas.
Bizning misolimizda jarima_hisobla(-5) holati refaktoring paytida
qo'shildi, lekin unga test yozilmadi. Kod uni qaytaradi, biroq
ertaga kimdir o'sha qatorni o'chirsa, hech qanday test yiqilmaydi.
Shuning uchun TDD dan keyin ham chegara qiymatlarini alohida ko'rib chiqish kerak. 14-bo'limda qoplamaning nima ko'rsatishi va nimani ko'rsatmasligini batafsil ko'ramiz.
Butun loyihani TDD bilan yozishga urinmang. Buning o'rniga keyingi nosozlikni TDD bilan tuzating:
- Xatoni ko'rsatadigan test yozing - u yiqilsin (qizil).
- Kodni tuzating - test o'tsin (yashil).
- Kodni tozalang - testlar yashil qolsin (refaktor).
Bu yerda "hali yo'q narsani testlash" muammosi ham yo'q: kod bor, faqat noto'g'ri ishlayapti. Shu usulda bir necha marta ishlagandan keyin tsikl odatga aylanadi.
- To'rtta tsiklni boshidan oxirigacha o'zingiz bajaring - har qadamda testni ishga tushiring.
- Birinchi tsiklda
return 0yozish nima uchun to'g'ri qadam ekanini tushuntiring. - Ikkinchi tsiklda qizil bosqichni o'tkazib yuboring - qanday xavf paydo bo'ladi?
- Uchinchi tsiklda eski testlar yiqilmaganini tekshiring.
- Manfiy kun uchun test yozing va u o'tishini ko'ring.
- Yangi qoida qo'shing: talabalar uchun jarima yarim baravar.
- Uni TDD bilan bajaring - avval test, keyin kod.
- Refaktoring qiling: yangi qoidani alohida funksiyaga chiqaring.
- Refaktoringdan keyin birorta test o'zgardimi?
- O'z loyihangizdagi keyingi nosozlikni TDD tartibida tuzating.
Xulosa #
- TDD - avval test, keyin kod yozish usuli; u Kent Beck tomonidan ommalashtirilgan.
- Tsikl uch qadamdan iborat: qizil (yiqiladigan test), yashil (eng oddiy yechim), refaktor (tozalash).
- Bitta tsikl 2-10 daqiqa davom etishi kerak; undan uzoq bo'lsa, qadam juda katta.
- Qizil bosqichni o'tkazib yubormang: yiqilgan testni ko'rmasangiz, u biror narsani tekshirayotganiga ishonchingiz bo'lmaydi.
- Import xatosi ham qizil bosqich hisoblanadi - modul hali yo'q.
- Birinchi tsiklda
return 0yozish "aldov" emas: uni rad etadigan test hali yo'q. - Har tsiklda bitta yangi qoida qo'shiladi, eskilar yashil qoladi.
- Refaktoring faqat hamma testlar yashil bo'lganda bajariladi va birorta testni o'zgartirmaydi.
- Robert Martin ning uch qoidasi: test yo'q - kod yo'q; test faqat yiqilishi uchun; kod faqat o'tkazish uchun.
- TDD kodni kichik tutadi: "keyin kerak bo'ladi" degan qismlar yozilmaydi.
- TDD biznes mantiq, hisob-kitob va nosozlik tuzatishda yaxshi ishlaydi.
- Tajribaviy va ko'rinishga bog'liq ishda TDD qiyin - avval tajriba qilib, keyin qaytadan yozish mumkin.
- TDD 100% xatti-harakat qoplamasini kafolatlamaydi - chegara qiymatlarini alohida ko'rib chiqish kerak.
- O'rganishning eng oson yo'li - keyingi nosozlikni TDD tartibida tuzatish.
Keyingi bo'limda darajani ko'taramiz: haqiqiy ma'lumotlar bazasi bilan integratsiya testlari.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.