Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Nima uchun mock
- 2.2. Test dublyorlari (test doubles)
- 2.3. Mock obyekti asoslari
- 2.4. return_value — nima qaytarishini belgilash
- 2.5. side_effect — kuchli boshqaruv
- 2.6. Chaqiruvni kuzatish (mock tekshiruvi)
- 2.7. spec — mock ni xavfsiz qilish
- 2.8. Mock ni haddan oshirmaslik
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Nima uchun mock: tashqi bog'liqlik
- Misol 2 — return_value, side_effect va chaqiruv kuzatuvi
- Misol 3 — Mock ni sozlash va spec
- Misol 4 — Amaliy: bog'liqlikni mock bilan test qilish
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
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
-
Mockobyekti — asoslari return_valuevaside_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:
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
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:
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) # 10side_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:
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
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_argsQoidalar
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 qilma4. Batafsil misollar
Misol 1 — Nima uchun mock: tashqi bog'liqlik
"""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:
=== 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 qiladiNima ko'rsatdi: 2.1, 2.3, 2.4-bo'limlar.
Misol 2 — return_value, side_effect va chaqiruv kuzatuvi
"""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:
=== 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'tdiNima ko'rsatdi: 2.4, 2.5, 2.6-bo'limlar.
Misol 3 — Mock ni sozlash va spec
"""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:
=== 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 ushlanadiNima 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.
"""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:
=== 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
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
m = Mock()
m.notogri_metod() # ❌ jim o'tadi
m = Mock(spec=Xizmat) # ✅ AttributeError3. return_value o'rniga side_effect kerak bo'lganda
m = Mock(return_value=[1, 2, 3]) # ❌ har chaqiruvda [1,2,3]
m = Mock(side_effect=[1, 2, 3]) # ✅ 1, keyin 2, keyin 34. Chaqiruvni tekshirmaslik
xizmat.yubor(xabar)
# ❌ yuborilganini tasdiqlamaslik
xizmat.yubor.assert_called_once_with(xabar) # ✅5. assert_called ni chaqiruvdan oldin
m.assert_called() # ❌ hali chaqirilmagan
m(); m.assert_called() # ✅6. Sof funksiyani mock qilish
qoshish = Mock(return_value=5) # ❌ oddiy funksiyani mock qilish keraksiz
assert qoshish_haqiqiy(2, 3) == 5 # ✅ to'g'ridan-to'g'ri test7. Xato yozilgan metod nomini spec siz
m = Mock()
m.tolov_qul() # ❌ (qil emas, qul) — jim o'tadi
m = Mock(spec=Xizmat); m.tolov_qul() # ✅ AttributeError7. 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 —
patchbilan 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
Faqat tashqi bog'liqlikni (API, vaqt, tasodif, baza) mock qiling.
return_value— oddiy javob;side_effect— ketma-ketlik/istisno/funksiya.Xato holatlarini
side_effect=Exceptionbilan simulyatsiya qiling.Chaqiruvni
assert_called_withbilan tasdiqlang.specbilan mock ni haqiqiy interfeysga bog'lang.Bog'liqlikni in'eksiya qiling — mock qilishni osonlashtiradi.
O'z kodingizni yoki sof funksiyalarni mock qilmang.
Imkon bo'lsa
fake(xotiradagi baza) ni mock dan afzal ko'ring.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
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
5 51 21call('a')FalseTrue{'x': 1}xatocall(2)6Mockbir martadan ko'p
Vazifa 2: Xatolarni tuzating
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:
passJavoblar
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):
api.harorat(shahar)ni mock qilib, aniq javob bering- Turli haroratlar (issiq, sovuq, muzlash) uchun xabar formatini tekshiring
- API xatosini (
side_effect=TimeoutError) simulyatsiya qiling api.haroratto'g'ri shahar bilan chaqirilganini tasdiqlang
Vazifa 4: Retry mantiqi
Qayta urinuvchi funksiya (3 marta urin) uchun:
side_effect=[Error, Error, muvaffaqiyat]bilan uchinchi urinishda muvaffaqiyatcall_count == 3ekanini tasdiqlang- Uch marta ham yiqilsa, xato tarqalishini tekshiring
- Birinchi urinishda muvaffaqiyat bo'lsa, faqat 1 marta chaqirilishini tekshiring
Vazifa 5: spec bilan interfeys
Omborinterfeysi (ol,saqla,ochir) yaratingspec=Omborbilan mock yarating- Har metodni sozlab, biznes mantiqni test qiling
- Noto'g'ri metod nomi
AttributeErrorberishini isbotlang
Vazifa 6: Test dublyorlarini solishtirish
Bir funksiyani (baza bilan ishlaydigan) uch usulda test qiling:
- Mock bilan (chaqiruvni tekshirish)
- Fake bilan (xotiradagi SQLite, 17.3)
- Stub bilan (faqat javob)
- 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
- Tashqi, boshqarib bo'lmaydigan bog'liqlik → mock yoki fake
- Xatti-harakatni tekshirish → fake afzal
- "Bu metod chaqirildimi" muhim bo'lsa → mock
- 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
- Mock — haqiqiy tashqi bog'liqlik uchun
- Over-mocking testni mo'rt qiladi
- Fake xatti-harakatni tekshiradi, refaktoringga chidamli
- "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:
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.return_valuevaside_effect.return_value— mock chaqirilganda qaytadigan oddiy javob (stub).side_effectkuchliroq: 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_argsvaassert_called_withbilan kodingiz bog'liqlikni to'g'ri chaqirganini tasdiqlaysiz.specva me'yor. OddiyMockhar atributni qabul qiladi va noto'g'ri metod nomini jim o'tkazadi —spec=HaqiqiySinfmock 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 tekshiradiganfake(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.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!