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.

🕑 17 daqiqa o‘qish 📄 877 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Qizil-yashil-refaktor
  2. Tsikl 1: kechikish yo'q
  3. Tsikl 2: kuniga 1000 so'm
  4. Tsikl 3: sakkizinchi kundan qimmatroq
  5. Tsikl 4: yuqori chegara
  6. Refaktor
  7. TDD nima beradi
  8. TDD qachon to'g'ri kelmaydi
  9. Xulosa

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 #

Har tsikl - bitta kichik imkoniyat 1. QIZIL Yiqiladigan test yozing 2. YASHIL Eng oddiy yechim bilan o'tkazing 3. REFAKTOR Kodni tozalang, testlar yashil Tsikl 2-10 daqiqa undan uzun bo'lsa, qadam juda katta Qizil bosqichni o'tkazib yubormang Yiqilgan testni ko'rmasangiz, u haqiqatan biror narsani tekshirayotganini bilmaysiz
Uchta qadam, har biri bir necha daqiqa

Endi buni amalda bajaramiz. Vazifa - kutubxonada kitob kechiktirilganda jarima hisoblash. Qoidalar:

KechikishJarima
0 kunYo'q
1-7 kunHar kunga 1000 so'm
8-kundan boshlabBirinchi 7 kun uchun 7000, keyin har kunga 2000
Juda uzoqJami 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:

Python
from jarima import jarima_hisobla


def test_kechikish_yoq_bolsa_jarima_yoq():
    assert jarima_hisobla(0) == 0
Terminal
pytest -q --tb=line test_jarima.py
Natija

================================ 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:

Python
def jarima_hisobla(kechikkan_kunlar):
    return 0
Terminal
pytest -q test_jarima.py
Natija
.                                                                [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:

Python
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
Terminal
pytest -q --tb=line test_jarima.py
Natija
.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:

Python
def jarima_hisobla(kechikkan_kunlar):
    return kechikkan_kunlar * 1000
Terminal
pytest -q test_jarima.py
Natija
...                                                              [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:

Python
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
Terminal
pytest -q --tb=line test_jarima.py
Natija
....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:

Python
def jarima_hisobla(kechikkan_kunlar):
    if kechikkan_kunlar <= 7:
        return kechikkan_kunlar * 1000
    return 7000 + (kechikkan_kunlar - 7) * 2000
Terminal
pytest -q test_jarima.py
Natija
......                                                           [100%]
6 passed in 0.01s

Tsikl 4: yuqori chegara #

Qizil:

Python
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
Terminal
pytest -q --tb=line test_jarima.py
Natija
.......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:

Python
def jarima_hisobla(kechikkan_kunlar):
    if kechikkan_kunlar <= 7:
        return kechikkan_kunlar * 1000
    return min(50_000, 7000 + (kechikkan_kunlar - 7) * 2000)
Terminal
pytest -q test_jarima.py
Natija
........                                                         [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:

Python
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)
Terminal
pytest -q test_jarima.py
Natija
........                                                         [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.

Uch qoida

Robert Martin TDD ni uchta qoidada ifodalagan:

  1. Yiqiladigan test bo'lmasa, ishlab chiqarish kodini yozmang.
  2. Testdan faqat yiqilishi uchun yetarlicha yozing (kompilyatsiya xatosi ham yiqilish hisoblanadi).
  3. 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 #

FoydaQanday paydo bo'ladi
To'liq qoplamaKodning har qatori test tufayli yozilgan
Oddiy dizaynOrtiqcha narsa yozilmaydi - talab qiluvchi test yo'q
Testlanadigan kodTest birinchi bo'lgani uchun kod unga moslashadi
Erta qaytish aloqasiXato yozilgandan keyin daqiqalar ichida ko'rinadi
Qo'rquvsiz refaktoringTarmoq mavjud - o'zgartirib ko'rish mumkin
Jonli hujjatTestlar - 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 #

TDD hamma joyda emas Yaxshi to'g'ri keladi • Qoidalari aniq biznes mantiq • Hisob-kitob, formatlash, tekshirish • Nosozlikni tuzatish • API va kutubxona interfeysi Qiyin yoki foydasiz • Hali qanday ko'rinishi noma'lum ish • Tajriba uchun yozilgan kod • Foydalanuvchi interfeysi ko'rinishi • Tashqi tizim bilan birinchi tanishuv Tajriba qilib bo'lgach, kodni tashlab, TDD bilan qaytadan yozish ham usul
TDD - qoida emas, vosita
TDD 100% qoplama kafolatlamaydi - va bu yetarli emas

"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.

TDD ni o'rganishning eng oson yo'li

Butun loyihani TDD bilan yozishga urinmang. Buning o'rniga keyingi nosozlikni TDD bilan tuzating:

  1. Xatoni ko'rsatadigan test yozing - u yiqilsin (qizil).
  2. Kodni tuzating - test o'tsin (yashil).
  3. 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.

Amaliy topshiriq
  1. To'rtta tsiklni boshidan oxirigacha o'zingiz bajaring - har qadamda testni ishga tushiring.
  2. Birinchi tsiklda return 0 yozish nima uchun to'g'ri qadam ekanini tushuntiring.
  3. Ikkinchi tsiklda qizil bosqichni o'tkazib yuboring - qanday xavf paydo bo'ladi?
  4. Uchinchi tsiklda eski testlar yiqilmaganini tekshiring.
  5. Manfiy kun uchun test yozing va u o'tishini ko'ring.
  6. Yangi qoida qo'shing: talabalar uchun jarima yarim baravar.
  7. Uni TDD bilan bajaring - avval test, keyin kod.
  8. Refaktoring qiling: yangi qoidani alohida funksiyaga chiqaring.
  9. Refaktoringdan keyin birorta test o'zgardimi?
  10. 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 0 yozish "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.

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.