IlmHamroh
Python kursi/Testlash5/10-dars19 daqiqa
Mundarija (22)

17.5-dars: Mock — tushuncha

17-QISM — TESTLASH · 5-dars


1. Kirish va motivatsiya

Shu paytgacha o'zimiz yozgan sof funksiyalarni test qildik: kirish beriladi, natija tekshiriladi. Lekin real kod ko'pincha tashqi dunyoga bog'liq: tarmoq so'rovi (API), joriy vaqt, tasodifiy son, ma'lumotlar bazasi, fayl tizimi, boshqa xizmat. Bularni to'g'ridan-to'g'ri test qilish qiyin — ular sekin, beqaror va nazoratdan tashqarida. Yechim — mock (soxta obyekt): tashqi bog'liqlikni test davomida boshqariladigan qalbaki obyekt bilan almashtirish.

Mock kodni izolyatsiyada test qilish imkonini beradi: siz aynan o'z kodingizni tekshirasiz, tashqi xizmat emas.

Real vaziyat. Bir jamoa ob-havo API sidan foydalanadigan funksiyani test qilmoqchi bo'ldi: harorat_xabari(shahar) API ga so'rov yuborib, "Toshkentda 25°C" qaytaradi. Testda muammolar: (1) API haqiqiy tarmoq talab qiladi — test sekin va internetga bog'liq; (2) harorat har kuni o'zgaradi — assert == "25°C" ertaga yiqiladi; (3) API pullik — har test pul sarflaydi; (4) API ishlamay qolsa test yiqiladi, garchi kod to'g'ri bo'lsa ham. Ular mock ga o'tishdi: API chaqiruvini soxta funksiya bilan almashtirib, unga aniq {"temp": 25} qaytarishni buyurishdi. Endi test tez, barqaror, tarmoqsiz va bepul — va aynan o'z kodini (xabar formatlash mantiqini) tekshiradi.

Bu darsda mock tushunchasini — tashqi bog'liqlikni boshqariladigan soxta bilan almashtirishni o'rganamiz.

Bu darsda:

  • Nima uchun mock — tashqi bog'liqlikdan izolyatsiya
  • Test dublyorlari: dummy, stub, mock, fake
  • Mock obyekti — asoslari
  • return_value va side_effect
  • Chaqiruvni kuzatish (called, call_count, assert_called_with)
  • spec — mock ni xavfsiz qilish
  • Mock ni haddan oshirmaslik
  • Amaliy: bog'liqlikni mock bilan test qilish

2. Nazariya — chuqur tushuntirish

2.1. Nima uchun mock

Ba'zi bog'liqliklar to'g'ridan-to'g'ri test qilishni qiyinlashtiradi:

Bog'liqlik Muammo
Tarmoq/API Sekin, internetga bog'liq, beqaror, pullik
Joriy vaqt (datetime.now) Har safar o'zgaradi — test takrorlanmaydi
Tasodif (random) Natija oldindan noma'lum
Ma'lumotlar bazasi Sozlash, tozalash, sekin
Fayl/tarmoq nosozligi Ataylab yuzaga keltirish qiyin
Boshqa xizmat Mavjud bo'lmasligi mumkin

Mock bularni boshqariladigan soxta bilan almashtiradi: siz aniq nima qaytishini (yoki qanday xato bo'lishini) belgilaysiz.

2.2. Test dublyorlari (test doubles)

"Mock" — umumiy nom; aslida bir necha tur bor:

Tur Nima qiladi
Dummy Faqat joyni to'ldiradi, ishlatilmaydi
Stub Oldindan belgilangan javob qaytaradi
Mock Javob qaytaradi va qanday chaqirilganini tekshiradi
Fake Ishlaydigan, lekin soddalashtirilgan (masalan xotiradagi baza)

Python'ning unittest.mock moduli bularning ko'pini Mock obyekti bilan qamrab oladi.

2.3. Mock obyekti asoslari

Mock — istalgan atribut va metodni "qabul qiladigan" universal soxta obyekt:

python
from unittest.mock import Mock

m = Mock()
m.istalgan_metod(1, 2)         # xato bermaydi — Mock o'zi yaratadi
print(m.istalgan_metod.called)         # True
print(m.istalgan_metod.call_count)     # 1
print(m.istalgan_metod.call_args)      # call(1, 2)

Mock har qanday atribut/metod chaqiruvini qabul qiladi va yozib boradi — keyin tekshirish uchun.

2.4. return_value — nima qaytarishini belgilash

python
m = Mock(return_value=42)
m()                            # 42

m.json.return_value = {"ism": "Aziz"}
m.json()                       # {"ism": "Aziz"}

return_value — mock chaqirilganda qaytadigan qiymat (stub xatti-harakati).

2.5. side_effect — kuchli boshqaruv

side_effect bir necha imkoniyat beradi:

python
m = Mock(side_effect=[1, 2, 3])        # har chaqiruvda navbatdagi
m(), m(), m()                          # 1, 2, 3

m = Mock(side_effect=ValueError("xato"))   # istisno ko'taradi
m()                                    # ValueError

m = Mock(side_effect=lambda x: x * 2)  # funksiya bilan hisoblash
m(5)                                   # 10
side_effect Natija
Ro'yxat Har chaqiruvda navbatdagi element
Istisno O'sha istisnoni ko'taradi
Funksiya Uni chaqirib natijasini qaytaradi

side_effect=Exception — tarmoq nosozligi, timeout kabi xato holatlarini test qilishning eng oson yo'li.

2.6. Chaqiruvni kuzatish (mock tekshiruvi)

Mock qanday chaqirilganini tasdiqlash:

Tekshiruv Ma'nosi
m.called Umuman chaqirildimi (bool)
m.call_count Necha marta
m.call_args Oxirgi chaqiruv argumentlari
m.call_args_list Barcha chaqiruvlar
m.assert_called() Kamida bir marta chaqirilgan
m.assert_called_once() Aynan bir marta
m.assert_called_with(...) Oxirgi chaqiruv shu argumentlar bilan
m.assert_called_once_with(...) Bir marta va shu argumentlar bilan
m.assert_not_called() Umuman chaqirilmagan

Bu — mock ni stub dan farqlaydi: mock nafaqat javob beradi, balki kodingiz uni to'g'ri chaqirganini tekshiradi.

2.7. spec — mock ni xavfsiz qilish

Oddiy Mock har qanday atributni qabul qiladi — hatto mavjud bo'lmagan metodni ham (xatoni yashiradi). spec mock ni haqiqiy sinfga bog'laydi:

python
m = Mock(spec=Xizmat)          # faqat Xizmat metodlariga ruxsat
m.mavjud_metod()               # OK
m.notogri_metod()              # AttributeError — xato ushlanadi

spec bilan mock haqiqiy obyekt kabi qat'iy bo'ladi — noto'g'ri metod nomi jim o'tmaydi.

2.8. Mock ni haddan oshirmaslik

To'g'ri Noto'g'ri
Tashqi bog'liqlikni mock qilish O'z kodingizni mock qilish
API, vaqt, tasodif, baza Sof funksiyalarni mock qilish
Xato holatlarini simulyatsiya Hamma narsani mock qilish

Haddan ziyod mock testlarni kodning ichki tuzilishiga bog'laydi: kod o'zgarsa, testlar yiqiladi, garchi xatti-harakat to'g'ri bo'lsa ham. Faqat haqiqiy tashqi bog'liqlikni mock qiling; imkon bo'lsa, fake (masalan xotiradagi SQLite, 17.3) afzalroq.


3. Tez ma'lumotnoma

python
from unittest.mock import Mock

m = Mock(return_value=42)              # chaqirilganda 42
m.json.return_value = {"ok": True}     # ichki metod
m2 = Mock(side_effect=[1, 2, 3])       # ketma-ket
m3 = Mock(side_effect=ConnectionError) # xato ko'taradi
m4 = Mock(spec=HaqiqiySinf)            # xavfsiz — faqat mavjud metodlar

# tekshirish
m.assert_called_once_with(1, 2)
m.call_count
m.call_args

Qoidalar

tashqi bog'liqlik (API, vaqt, tasodif, baza) — mock
return_value — stub javob
side_effect — ketma-ketlik, istisno, funksiya
xato holati — side_effect=Exception
chaqiruvni assert_called_with bilan tekshir
spec bilan xavfsiz qil
o'z kodingni yoki sof funksiyani mock qilma

4. Batafsil misollar

Misol 1 — Nima uchun mock: tashqi bog'liqlik

python
"""tashqi bog'liqlik (vaqt, tasodif) testni qiyinlashtiradi; mock uni boshqariladigan qiladi; return_value bilan aniq javob; test barqaror bo'ladi."""

from unittest.mock import Mock


def salomlashuv(soat_manbai) -> str:
    """Vaqtga qarab salomlashadi — soat_manbai() joriy soatni qaytaradi."""
    soat = soat_manbai()
    if soat < 12:
        return "Xayrli tong"
    if soat < 18:
        return "Xayrli kun"
    return "Xayrli kech"


def tavakkal_bonus(tasodif_manbai, asosiy: int) -> int:
    """Tasodifiy bonus qo'shadi — tasodif_manbai() 0-100 qaytaradi."""
    return asosiy + tasodif_manbai()


def main() -> None:
    print("=== 1. ⚠️ Haqiqiy vaqt bilan test qiyin ===")
    print("  datetime.now() har safar o'zgaradi — 'Xayrli tong' ertaga yiqiladi")

    print("\n=== 2. Mock bilan vaqtni boshqarish ===")
    for soat, kutilgan in [(9, "Xayrli tong"), (14, "Xayrli kun"), (20, "Xayrli kech")]:
        soat_mock = Mock(return_value=soat)
        natija = salomlashuv(soat_mock)
        belgi = "✅" if natija == kutilgan else "❌"
        print(f"  {belgi} soat {soat:2} → {natija}")
        print(f"     soat_manbai chaqirildimi: {soat_mock.called}, necha marta: {soat_mock.call_count}")

    print("\n=== 3. Chegara holatlari (mock bilan aniq) ===")
    for soat, kutilgan in [(11, "Xayrli tong"), (12, "Xayrli kun"), (17, "Xayrli kun"), (18, "Xayrli kech")]:
        natija = salomlashuv(Mock(return_value=soat))
        print(f"  soat {soat}: {natija} ({'✅' if natija == kutilgan else '❌'})")

    print("\n=== 4. Tasodifni boshqarish ===")
    tasodif_mock = Mock(return_value=50)
    natija = tavakkal_bonus(tasodif_mock, 100)
    print(f"  asosiy 100 + soxta tasodif 50 = {natija}")
    print(f"  tasodif_manbai chaqirildi: {tasodif_mock.called}")
    print("  ⭐ haqiqiy random bilan natija oldindan noma'lum — mock uni aniq qiladi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. ⚠️ Haqiqiy vaqt bilan test qiyin ===
  datetime.now() har safar o'zgaradi — 'Xayrli tong' ertaga yiqiladi

=== 2. Mock bilan vaqtni boshqarish ===
  ✅ soat  9 → Xayrli tong
     soat_manbai chaqirildimi: True, necha marta: 1
  ✅ soat 14 → Xayrli kun
     soat_manbai chaqirildimi: True, necha marta: 1
  ✅ soat 20 → Xayrli kech
     soat_manbai chaqirildimi: True, necha marta: 1

=== 3. Chegara holatlari (mock bilan aniq) ===
  soat 11: Xayrli tong (✅)
  soat 12: Xayrli kun (✅)
  soat 17: Xayrli kun (✅)
  soat 18: Xayrli kech (✅)

=== 4. Tasodifni boshqarish ===
  asosiy 100 + soxta tasodif 50 = 150
  tasodif_manbai chaqirildi: True
  ⭐ haqiqiy random bilan natija oldindan noma'lum — mock uni aniq qiladi

Nima ko'rsatdi: 2.1, 2.3, 2.4-bo'limlar.

Misol 2 — return_value, side_effect va chaqiruv kuzatuvi

python
"""return_value; side_effect ro'yxat, istisno, funksiya; called/call_count/call_args; assert_called_with; xato holatini simulyatsiya."""

from unittest.mock import Mock, call


def main() -> None:
    print("=== 1. return_value ===")
    m = Mock(return_value=42)
    print(f"  m('x') = {m('x')}, m() = {m()}")
    print(f"  call_count: {m.call_count}, oxirgi args: {m.call_args}")

    print("\n=== 2. side_effect — ro'yxat (ketma-ket) ===")
    navbat = Mock(side_effect=[10, 20, 30])
    print(f"  {navbat()}, {navbat()}, {navbat()}")

    print("\n=== 3. side_effect — istisno (xato holati) ===")
    tarmoq = Mock(side_effect=ConnectionError("tarmoq yo'q"))
    try:
        tarmoq()
    except ConnectionError as xato:
        print(f"  ConnectionError simulyatsiya qilindi: {xato}")

    print("\n=== 4. side_effect — funksiya ===")
    hisob = Mock(side_effect=lambda x: x * 2)
    print(f"  hisob(5) = {hisob(5)}, hisob(10) = {hisob(10)}")

    print("\n=== 5. Chaqiruvni kuzatish ===")
    xizmat = Mock()
    xizmat.yubor("salom", muhim=True)
    xizmat.yubor("xayr")
    print(f"  called: {xizmat.yubor.called}, count: {xizmat.yubor.call_count}")
    print(f"  oxirgi chaqiruv: {xizmat.yubor.call_args}")
    print(f"  barcha chaqiruvlar: {xizmat.yubor.call_args_list}")
    print(f"  kutilgan ketma-ketlik: {xizmat.yubor.call_args_list == [call('salom', muhim=True), call('xayr')]}")

    print("\n=== 6. assert_called_* ===")
    m2 = Mock()
    m2(1, 2, uch=3)
    m2.assert_called_once()
    m2.assert_called_with(1, 2, uch=3)
    print("  assert_called_once() va assert_called_with(1, 2, uch=3) — o'tdi")
    try:
        m2.assert_called_with(9, 9)
    except AssertionError:
        print("  assert_called_with(9, 9) — to'g'ri yiqildi (boshqa argumentlar)")

    print("\n=== 7. assert_not_called ===")
    ishlatilmagan = Mock()
    ishlatilmagan.assert_not_called()
    print("  ishlatilmagan mock — assert_not_called() o'tdi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. return_value ===
  m('x') = 42, m() = 42
  call_count: 2, oxirgi args: call()

=== 2. side_effect — ro'yxat (ketma-ket) ===
  10, 20, 30

=== 3. side_effect — istisno (xato holati) ===
  ConnectionError simulyatsiya qilindi: tarmoq yo'q

=== 4. side_effect — funksiya ===
  hisob(5) = 10, hisob(10) = 20

=== 5. Chaqiruvni kuzatish ===
  called: True, count: 2
  oxirgi chaqiruv: call('xayr')
  barcha chaqiruvlar: [call('salom', muhim=True), call('xayr')]
  kutilgan ketma-ketlik: True

=== 6. assert_called_* ===
  assert_called_once() va assert_called_with(1, 2, uch=3) — o'tdi
  assert_called_with(9, 9) — to'g'ri yiqildi (boshqa argumentlar)

=== 7. assert_not_called ===
  ishlatilmagan mock — assert_not_called() o'tdi

Nima ko'rsatdi: 2.4, 2.5, 2.6-bo'limlar.

Misol 3 — Mock ni sozlash va spec

python
"""atributlarni sozlash; ichki metod (json.return_value); spec bilan xavfsiz mock; noto'g'ri metod AttributeError beradi; oddiy Mock jim o'tkazadi."""

from unittest.mock import Mock


class TolovXizmati:
    """Haqiqiy to'lov xizmati interfeysi."""
    def tolov_qil(self, summa: int, karta: str) -> dict: ...
    def bekor_qil(self, tranzaksiya_id: str) -> bool: ...


def main() -> None:
    print("=== 1. Atributlarni sozlash ===")
    javob = Mock()
    javob.status_code = 200
    javob.json.return_value = {"ism": "Aziz", "balans": 50000}
    print(f"  status: {javob.status_code}")
    print(f"  json(): {javob.json()}")
    print(f"  json ichki metod chaqirildi: {javob.json.called}")

    print("\n=== 2. Ichma-ich sozlash ===")
    mijoz = Mock()
    mijoz.hisob.balans.return_value = 100000
    print(f"  mijoz.hisob.balans() = {mijoz.hisob.balans()}")

    print("\n=== 3. ⚠️ Oddiy Mock xatoni yashiradi ===")
    oddiy = Mock()
    natija = oddiy.mavjud_bolmagan_metod()          # xato bermaydi!
    print(f"  oddiy.mavjud_bolmagan_metod() → {type(natija).__name__} (jim o'tdi — xavfli)")

    print("\n=== 4. spec — xavfsiz mock ===")
    tolov = Mock(spec=TolovXizmati)
    tolov.tolov_qil.return_value = {"holat": "muvaffaqiyat", "id": "TXN-1"}
    print(f"  to'g'ri metod: {tolov.tolov_qil(50000, '8600...')}")
    print(f"  bekor_qil ham bor: {hasattr(tolov, 'bekor_qil')}")
    try:
        tolov.notogri_metod()
    except AttributeError as xato:
        print(f"  notogri_metod() → AttributeError: {str(xato).split(chr(39))[0].strip()}")
    print("  ⭐ spec bilan noto'g'ri metod nomi darhol ushlanadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Atributlarni sozlash ===
  status: 200
  json(): {'ism': 'Aziz', 'balans': 50000}
  json ichki metod chaqirildi: True

=== 2. Ichma-ich sozlash ===
  mijoz.hisob.balans() = 100000

=== 3. ⚠️ Oddiy Mock xatoni yashiradi ===
  oddiy.mavjud_bolmagan_metod() → Mock (jim o'tdi — xavfli)

=== 4. spec — xavfsiz mock ===
  to'g'ri metod: {'holat': 'muvaffaqiyat', 'id': 'TXN-1'}
  bekor_qil ham bor: True
  notogri_metod() → AttributeError: Mock object has no attribute
  ⭐ spec bilan noto'g'ri metod nomi darhol ushlanadi

Nima ko'rsatdi: 2.4, 2.7-bo'limlar.

Misol 4 — Amaliy: bog'liqlikni mock bilan test qilish

Buyurtma xizmatini test qilamiz: u to'lov xizmati (tashqi API) va xabar yuboruvchi (SMS) ga bog'liq. Bu bog'liqliklarni in'eksiya qilamiz 13.5-bob — konstruktor orqali beramiz. Testda ularni mock bilan almashtirib, buyurtma mantiqini izolyatsiyada tekshiramiz: to'lov muvaffaqiyatli/muvaffaqiyatsiz holatlari, xabar yuborilganini tasdiqlash, xato yo'llari. Bu — mock ning eng keng tarqalgan real qo'llanishi.

python
"""bog'liqlik in'eksiyasi + mock: to'lov va SMS mock qilinadi; muvaffaqiyat/muvaffaqiyatsizlik; assert_called_with bilan to'g'ri chaqiruv tasdiqlanadi; xato holati side_effect bilan."""

from unittest.mock import Mock


class BuyurtmaXizmati:
    def __init__(self, tolov_xizmati, xabar_xizmati):
        self.tolov = tolov_xizmati
        self.xabar = xabar_xizmati

    def buyurtma_ber(self, mijoz: str, summa: int, telefon: str) -> dict:
        if summa <= 0:
            raise ValueError("summa musbat bo'lishi kerak")
        natija = self.tolov.tolov_qil(summa)
        if natija["holat"] != "muvaffaqiyat":
            self.xabar.yubor(telefon, "To'lov amalga oshmadi")
            return {"holat": "rad", "sabab": natija.get("sabab", "nomalum")}
        self.xabar.yubor(telefon, f"Buyurtmangiz qabul qilindi, {summa} so'm")
        return {"holat": "qabul", "tranzaksiya": natija["id"]}


def main() -> None:
    print("=== 1. Muvaffaqiyatli buyurtma ===")
    tolov = Mock()
    tolov.tolov_qil.return_value = {"holat": "muvaffaqiyat", "id": "TXN-100"}
    xabar = Mock()
    xizmat = BuyurtmaXizmati(tolov, xabar)

    natija = xizmat.buyurtma_ber("Aziz", 50000, "+998901234567")
    print(f"  natija: {natija}")
    print(f"  to'lov chaqirildi: {tolov.tolov_qil.call_args}")
    print(f"  xabar chaqirildi: {xabar.yubor.call_args}")
    xabar.yubor.assert_called_once_with("+998901234567", "Buyurtmangiz qabul qilindi, 50000 so'm")
    print("  ⭐ assert_called_once_with — to'g'ri xabar yuborilgani tasdiqlandi")

    print("\n=== 2. To'lov rad etildi ===")
    tolov2 = Mock()
    tolov2.tolov_qil.return_value = {"holat": "rad", "sabab": "mablag' yetarli emas"}
    xabar2 = Mock()
    xizmat2 = BuyurtmaXizmati(tolov2, xabar2)

    natija = xizmat2.buyurtma_ber("Bek", 99999, "+998907654321")
    print(f"  natija: {natija}")
    print(f"  xato xabari yuborildi: {xabar2.yubor.call_args}")

    print("\n=== 3. To'lov xizmati ishlamayapti (side_effect) ===")
    tolov3 = Mock()
    tolov3.tolov_qil.side_effect = ConnectionError("to'lov serveri javob bermayapti")
    xizmat3 = BuyurtmaXizmati(tolov3, Mock())
    try:
        xizmat3.buyurtma_ber("Malika", 30000, "+998901112233")
    except ConnectionError as xato:
        print(f"  ConnectionError to'g'ri tarqaldi: {xato}")

    print("\n=== 4. Noto'g'ri summa — tashqi xizmat chaqirilmaydi ===")
    tolov4 = Mock()
    xabar4 = Mock()
    xizmat4 = BuyurtmaXizmati(tolov4, xabar4)
    try:
        xizmat4.buyurtma_ber("Nodira", -100, "+998900000000")
    except ValueError as xato:
        print(f"  ValueError: {xato}")
    tolov4.tolov_qil.assert_not_called()
    xabar4.yubor.assert_not_called()
    print("  ⭐ validatsiya yiqilganda to'lov va xabar UMUMAN chaqirilmadi (assert_not_called)")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Muvaffaqiyatli buyurtma ===
  natija: {'holat': 'qabul', 'tranzaksiya': 'TXN-100'}
  to'lov chaqirildi: call(50000)
  xabar chaqirildi: call('+998901234567', "Buyurtmangiz qabul qilindi, 50000 so'm")
  ⭐ assert_called_once_with — to'g'ri xabar yuborilgani tasdiqlandi

=== 2. To'lov rad etildi ===
  natija: {'holat': 'rad', 'sabab': "mablag' yetarli emas"}
  xato xabari yuborildi: call('+998907654321', "To'lov amalga oshmadi")

=== 3. To'lov xizmati ishlamayapti (side_effect) ===
  ConnectionError to'g'ri tarqaldi: to'lov serveri javob bermayapti

=== 4. Noto'g'ri summa — tashqi xizmat chaqirilmaydi ===
  ValueError: summa musbat bo'lishi kerak
  ⭐ validatsiya yiqilganda to'lov va xabar UMUMAN chaqirilmadi (assert_not_called)

Nima ko'rsatdi: 2.4, 2.5, 2.6, 2.8-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Mock hamma narsani test qiladi" Faqat tashqi bog'liqlikni almashtiradi
"Mock haqiqiy kodni ishga tushiradi" Yo'q — soxta javob beradi
"Mock va stub bir xil" Stub javob beradi; mock chaqiruvni ham tekshiradi
"Mock() xavfsiz" Har atributni qabul qiladi — spec kerak
"return_value va side_effect bir xil" side_effect — ketma-ketlik/istisno/funksiya
"Xato holatini test qilib bo'lmaydi" side_effect=Exception bilan mumkin
"Ko'proq mock — yaxshiroq test" Haddan ziyod mock testni mo'rt qiladi
"O'z kodingni ham mock qil" Faqat tashqi bog'liqlikni

6. Keng tarqalgan xatolar va yechimlari

1. Haqiqiy tarmoq/vaqt bilan test

python
def test_salom():
    assert salomlashuv(datetime.now().hour) == "Xayrli kun"   # ❌ vaqtga bog'liq
def test_salom():
    assert salomlashuv(Mock(return_value=14)) == "Xayrli kun" # ✅

2. Mock() ni spec siz

python
m = Mock()
m.notogri_metod()                                   # ❌ jim o'tadi
m = Mock(spec=Xizmat)                               # ✅ AttributeError

3. return_value o'rniga side_effect kerak bo'lganda

python
m = Mock(return_value=[1, 2, 3])                    # ❌ har chaqiruvda [1,2,3]
m = Mock(side_effect=[1, 2, 3])                     # ✅ 1, keyin 2, keyin 3

4. Chaqiruvni tekshirmaslik

python
xizmat.yubor(xabar)
# ❌ yuborilganini tasdiqlamaslik
xizmat.yubor.assert_called_once_with(xabar)         # ✅

5. assert_called ni chaqiruvdan oldin

python
m.assert_called()                                   # ❌ hali chaqirilmagan
m(); m.assert_called()                              # ✅

6. Sof funksiyani mock qilish

python
qoshish = Mock(return_value=5)                      # ❌ oddiy funksiyani mock qilish keraksiz
assert qoshish_haqiqiy(2, 3) == 5                   # ✅ to'g'ridan-to'g'ri test

7. Xato yozilgan metod nomini spec siz

python
m = Mock()
m.tolov_qul()                                       # ❌ (qil emas, qul) — jim o'tadi
m = Mock(spec=Xizmat); m.tolov_qul()                # ✅ AttributeError

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 13.5-dars (o'tilgan): dependency injection — mock ni in'eksiya qilish
  • 17.3-dars (o'tilgan): monkeypatch — mock ning fixture varianti
  • 17.6-dars: mock amaliyoti — patch bilan mavjud kodni almashtirish
  • 17.9-dars: integratsion testlar — qachon mock, qachon haqiqiy
  • 20.15-dars: FastAPI — tashqi xizmatlarni mock qilish
  • 26.7-dars: API integratsiyasi — mock bilan test

8. Eng yaxshi amaliyotlar

  1. Faqat tashqi bog'liqlikni (API, vaqt, tasodif, baza) mock qiling.

  2. return_value — oddiy javob; side_effect — ketma-ketlik/istisno/funksiya.

  3. Xato holatlarini side_effect=Exception bilan simulyatsiya qiling.

  4. Chaqiruvni assert_called_with bilan tasdiqlang.

  5. spec bilan mock ni haqiqiy interfeysga bog'lang.

  6. Bog'liqlikni in'eksiya qiling — mock qilishni osonlashtiradi.

  7. O'z kodingizni yoki sof funksiyalarni mock qilmang.

  8. Imkon bo'lsa fake (xotiradagi baza) ni mock dan afzal ko'ring.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
from unittest.mock import Mock
1.  m = Mock(return_value=5); print(m(), m())
2.  m = Mock(side_effect=[1, 2]); print(m(), m())
3.  m = Mock(); m(1, 2); print(m.call_count)
4.  m = Mock(); m("a"); print(m.call_args)
5.  m = Mock(); print(m.called)
6.  m = Mock(return_value=10); m(); print(m.called)
7.  m = Mock(); m.json.return_value = {"x": 1}; print(m.json())
8.  m = Mock(side_effect=ValueError("e"))
    try: m()
    except ValueError: print("xato")
9.  m = Mock(); m(1); m(2)
    print(m.call_args)          # oxirgi
10. m = Mock(side_effect=lambda x: x + 1); print(m(5))
11. m = Mock(); print(type(m.istalgan).__name__)
12. m = Mock(); m(1); m(1)
    try: m.assert_called_once()
    except AssertionError: print("bir martadan ko'p")
Javoblar
  1. 5 5
  2. 1 2
  3. 1
  4. call('a')
  5. False
  6. True
  7. {'x': 1}
  8. xato
  9. call(2)
  10. 6
  11. Mock
  12. bir martadan ko'p

Vazifa 2: Xatolarni tuzating

python
1.  def test_salom():
        assert salomlashuv(datetime.now().hour) in ("Xayrli tong", "Xayrli kun", "Xayrli kech")

2.  def test_xizmat():
        m = Mock()
        natija = m.tolov_qul(100)           # metod nomi xato
        assert natija is not None

3.  m = Mock(return_value=[1, 2, 3])
    # har chaqiruvda navbatdagi element kerak

4.  xizmat = Mock()
    buyurtma_ber(xizmat)
    # xizmat.yubor chaqirilganini tekshirish yo'q

5.  def test_tarmoq_xatosi():
        m = Mock(return_value=ConnectionError())   # xato QAYTARADI, ko'tarmaydi
        try:
            api_chaqir(m)
        except ConnectionError:
            pass
Javoblar
python
1.  def test_salom():
        assert salomlashuv(Mock(return_value=9)) == "Xayrli tong"   # aniq

2.  def test_xizmat():
        m = Mock(spec=TolovXizmati)          # spec — xato metod ushlanadi
        m.tolov_qil.return_value = {"ok": True}
        assert m.tolov_qil(100)["ok"]

3.  m = Mock(side_effect=[1, 2, 3])          # side_effect

4.  xizmat = Mock()
    buyurtma_ber(xizmat)
    xizmat.yubor.assert_called_once()        # tekshirish qo'shildi

5.  def test_tarmoq_xatosi():
        m = Mock(side_effect=ConnectionError())   # side_effect — ko'taradi
        with pytest.raises(ConnectionError):
            api_chaqir(m)

Vazifa 3: Ob-havo xizmati

harorat_xabari(shahar, api) funksiyasi uchun (api — in'eksiya qilingan):

  1. api.harorat(shahar) ni mock qilib, aniq javob bering
  2. Turli haroratlar (issiq, sovuq, muzlash) uchun xabar formatini tekshiring
  3. API xatosini (side_effect=TimeoutError) simulyatsiya qiling
  4. api.harorat to'g'ri shahar bilan chaqirilganini tasdiqlang

Vazifa 4: Retry mantiqi

Qayta urinuvchi funksiya (3 marta urin) uchun:

  1. side_effect=[Error, Error, muvaffaqiyat] bilan uchinchi urinishda muvaffaqiyat
  2. call_count == 3 ekanini tasdiqlang
  3. Uch marta ham yiqilsa, xato tarqalishini tekshiring
  4. Birinchi urinishda muvaffaqiyat bo'lsa, faqat 1 marta chaqirilishini tekshiring

Vazifa 5: spec bilan interfeys

  1. Ombor interfeysi (ol, saqla, ochir) yarating
  2. spec=Ombor bilan mock yarating
  3. Har metodni sozlab, biznes mantiqni test qiling
  4. Noto'g'ri metod nomi AttributeError berishini isbotlang

Vazifa 6: Test dublyorlarini solishtirish

Bir funksiyani (baza bilan ishlaydigan) uch usulda test qiling:

  1. Mock bilan (chaqiruvni tekshirish)
  2. Fake bilan (xotiradagi SQLite, 17.3)
  3. Stub bilan (faqat javob)
  4. Har birining afzallik va kamchiligini yozing

Vazifa 7: O'ylash

Mock kuchli vosita, lekin "mock hamma narsani" yondashuvi mo'rt testlarga olib keladi — kod o'zgarsa, testlar yiqiladi, garchi dastur to'g'ri ishlasa ham. Bu "over-mocking" muammosi. Qachon mock to'g'ri tanlov, qachon u testni kodning ichki tuzilishiga haddan ziyod bog'laydi, va "fake" (haqiqiy, soddalashtirilgan implementatsiya) qachon afzal?

Javob

Qisqa javob: Mock haqiqiy tashqi bog'liqlik (tarmoq, vaqt, tasodif, uchinchi tomon xizmati) uchun to'g'ri — bularni test muhitida ishlatib bo'lmaydi. Lekin mock testni kodning qanday ishlashiga (ichki chaqiruvlar ketma-ketligiga) bog'lasa, kod refaktoring qilinganda test yiqiladi, garchi natija to'g'ri bo'lsa ham. Bunday holatda fake (soddalashtirilgan, lekin ishlaydigan implementatsiya, masalan xotiradagi baza) afzal — u xatti-harakatni tekshiradi, tuzilishni emas.

1. Mock to'g'ri bo'lgan holatlar

Holat Sabab
Tashqi API Tarmoq, pul, beqarorlik
Vaqt/tasodif Takrorlanmaydi
Xato holatlari Ataylab yuzaga keltirish qiyin
Sekin resurs Test tez bo'lishi kerak
Yon ta'sir (email, SMS) Haqiqatan yuborilmasligi kerak

2. Over-mocking belgilari

Belgi Muammo
Har bog'liqlik mock Test kod tuzilishiga bog'lanadi
Ichki metod chaqiruvlarini tekshirish Refaktoring test'ni buzadi
Mock ni mock qaytaradi Test o'qib bo'lmaydi
Test kodni takrorlaydi Nima tekshirilyapti — noaniq

3. Mock vs Fake

Mezon Mock Fake
Nima tekshiradi Chaqiruvlar (tuzilish) Natija (xatti-harakat)
Refaktoringga chidamli Kam Ko'p
Sozlash Har test'da Bir marta
Ishonch Interfeys to'g'ri chaqirilgani Haqiqiy xatti-harakat
Misol Mock(spec=Baza) Xotiradagi SQLite

4. Amaliy qoida

  1. Tashqi, boshqarib bo'lmaydigan bog'liqlik → mock yoki fake
  2. Xatti-harakatni tekshirish → fake afzal
  3. "Bu metod chaqirildimi" muhim bo'lsa → mock
  4. O'z kodingiz, sof funksiya → mock kerak emas

5. "Xatti-harakatni test qil, tuzilishni emas"

Eng yaxshi testlar nima qilinganini (natija) tekshiradi, qanday qilinganini (ichki chaqiruvlar) emas. Mock ba'zan tuzilishga bog'lanishga majbur qiladi — shuning uchun imkon bo'lsa fake yoki haqiqiy (arzon) bog'liqlik afzal.

6. Xulosa

  1. Mock — haqiqiy tashqi bog'liqlik uchun
  2. Over-mocking testni mo'rt qiladi
  3. Fake xatti-harakatni tekshiradi, refaktoringga chidamli
  4. "Nima" ni test qiling, "qanday" ni emas

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda mock tushunchasini — tashqi bog'liqlikni boshqariladigan soxta bilan almashtirishni o'rgandik.

Eng muhim uch fikr:

  1. Mock tashqi bog'liqlikdan izolyatsiya beradi. Tarmoq, joriy vaqt, tasodif, baza — bularni to'g'ridan-to'g'ri test qilish sekin, beqaror va nazoratdan tashqarida. Mock ularni boshqariladigan soxta obyekt bilan almashtiradi: siz aynan o'z kodingizni tekshirasiz, tashqi xizmat emas. Mock() istalgan atribut/metod chaqiruvini qabul qiladi va yozib boradi.

  2. return_value va side_effect. return_value — mock chaqirilganda qaytadigan oddiy javob (stub). side_effect kuchliroq: ro'yxat (har chaqiruvda navbatdagi), istisno (side_effect=ConnectionError — xato holatlarini simulyatsiya qilishning eng oson yo'li) yoki funksiya. Mock ni stub dan farqlovchi jihat — chaqiruvni kuzatish: called, call_count, call_args va assert_called_with bilan kodingiz bog'liqlikni to'g'ri chaqirganini tasdiqlaysiz.

  3. spec va me'yor. Oddiy Mock har atributni qabul qiladi va noto'g'ri metod nomini jim o'tkazadi — spec=HaqiqiySinf mock ni haqiqiy interfeysga bog'lab, xatolarni ushlaydi. Va eng muhimi: mock ni haddan oshirmang — faqat haqiqiy tashqi bog'liqlikni mock qiling, o'z kodingizni yoki sof funksiyalarni emas; imkon bo'lsa, xatti-harakatni tekshiradigan fake (masalan xotiradagi baza) mock dan afzal.

Keyingi darsda mock amaliyotini o'rganamiz: patch bilan mavjud koddagi bog'liqlikni almashtirish, "qayerda ishlatilsa, o'sha yerda patch qilish" qoidasi va autospec — real kodni mock qilishning amaliy usullari.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
17.5-dars: Mock — tushuncha — IlmHamroh