Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. patch — asosiy shakllar
- 2.2. "Qayerda ishlatilsa, o'sha yerda patch qil"
- 2.3. patch.object — obyekt atributini
- 2.4. patch.dict — lug'atni
- 2.5. autospec — xavfsiz patch
- 2.6. MagicMock — sehrli metodlar
- 2.7. Bir necha patch
- 2.8. Qachon patch, qachon in'eksiya
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — patch: kontekst menejeri va dekorator
- Misol 2 — Qayerda patch qilish kerak
- Misol 3 — patch.dict, autospec va MagicMock
- Misol 4 — Amaliy: tarmoq chaqiruvli modulni 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.6-dars: Mock — amaliyot
17-QISM — TESTLASH · 6-dars
1. Kirish va motivatsiya
O'tgan darsda mock tushunchasini o'rgandik: tashqi bog'liqlikni soxta obyekt bilan almashtirish. U yerda bog'liqlikni in'eksiya qildik — funksiyaga parametr sifatida uzatib. Lekin real kod ko'pincha bog'liqlikni to'g'ridan-to'g'ri chaqiradi: requests.get(...), datetime.now(), open(...). Bularni in'eksiya qilinmagan bo'lsa qanday almashtirasiz? Javob — patch: mavjud koddagi obyektni test davomida vaqtincha soxta bilan almashtirish.
patch — mock ning eng amaliy vositasi, lekin unda bitta mashhur tuzoq bor: qayerda patch qilish kerak. Bu tuzoqni tushunmaslik — mock bilan bog'liq eng ko'p uchraydigan xato.
Real vaziyat. Bir jamoa ob-havo funksiyasini test qilardi. Funksiya from ob_havo_api import haroratni_ol qilib, keyin haroratni_ol(shahar) chaqirardi. Ular patch("ob_havo_api.haroratni_ol", return_value=25) yozishdi — lekin test haqiqiy tarmoqqa chiqishda davom etdi va beqaror bo'ldi. Sabab: modul allaqachon haroratni_ol ni o'z nomlar fazosiga import qilgan, va patch asl joyni o'zgartirganda ular kutgan nusxaga ta'sir qilmadi. Yechim: patch("ular_moduli.haroratni_ol", ...) — qayerda ishlatilsa, o'sha yerda patch qilish. Bu bitta qoidani anglash mock bilan ishlashni butunlay o'zgartirdi.
Bu darsda patch bilan mavjud koddagi bog'liqlikni almashtirishni va "qayerda patch qilish" qoidasini o'rganamiz.
Bu darsda:
-
patch— kontekst menejeri va dekorator - "Qayerda ishlatilsa, o'sha yerda patch qil"
patch.object— obyekt atributinipatch.dict— lug'atni (masalanos.environ)autospec— xavfsiz patchMagicMock— sehrli metodlar- Bir necha patch birga
- Amaliy: tarmoq chaqiruvli modulni test qilish
2. Nazariya — chuqur tushuntirish
2.1. patch — asosiy shakllar
patch mavjud obyektni test davomida almashtiradi va keyin avtomatik tiklaydi:
Kontekst menejeri sifatida:
from unittest.mock import patch
def test_xabar():
with patch("app.haroratni_ol", return_value=25) as mock_h:
assert app.xabar("Toshkent") == "Toshkent: 25°C"
mock_h.assert_called_once_with("Toshkent")Dekorator sifatida:
@patch("app.haroratni_ol", return_value=25)
def test_xabar(mock_h): # mock argument sifatida keladi
assert "25" in app.xabar("Toshkent") Dekoratorda mock pastdan yuqoriga argument bo'lib keladi (bir necha @patch bo'lsa, tartibga e'tibor bering).
2.2. "Qayerda ishlatilsa, o'sha yerda patch qil"
Bu — mock ning eng muhim qoidasi. Patch obyekt ta'riflangan joyni emas, ishlatiladigan joyni o'zgartiradi:
# app.py
from vaqt import hozir_soat # 'hozir_soat' endi app nomlar fazosida
def salom():
return hozir_soat()| Patch | Natija |
|---|---|
patch("app.hozir_soat") |
Ishlaydi — app ishlatadigan nusxa |
patch("vaqt.hozir_soat") |
Ishlamaydi — app allaqachon import qilgan |
Sabab: from vaqt import hozir_soat app modulida yangi nom yaratadi. app.salom() shu nomni ishlatadi, vaqt.hozir_soat ni emas. Patch app.hozir_soat ni almashtirishi kerak.
Agar kod import vaqt qilib vaqt.hozir_soat() chaqirsa, u holda patch("vaqt.hozir_soat") to'g'ri — chunki ishlatilgan joy vaqt.hozir_soat.
2.3. patch.object — obyekt atributini
Butun yo'l o'rniga aniq obyekt va atribut nomi:
import app
with patch.object(app, "haroratni_ol", return_value=25):
...
# metodni patch qilish
with patch.object(TolovXizmati, "tolov_qil", return_value={"ok": True}):
... patch.object — obyekt allaqachon qo'lda bo'lsa qulayroq va IDE tekshiruviga do'st.
2.4. patch.dict — lug'atni
Lug'atni (masalan muhit o'zgaruvchilari) vaqtincha o'zgartirish:
with patch.dict(os.environ, {"REJIM": "test", "DEBUG": "1"}):
... # shu blokda o'zgargan
# blokdan keyin os.environ tiklanadi patch.dict — monkeypatch.setenv 17.3-bob ning pytest siz varianti.
2.5. autospec — xavfsiz patch
Oddiy patch mock ni spec siz yaratadi — noto'g'ri metod nomi jim o'tadi. autospec=True mock ni asl obyekt imzosiga bog'laydi:
with patch("app.tolov_qil", autospec=True) as m:
m.return_value = {"ok": True}
# noto'g'ri argumentlar bilan chaqirilsa xato beradi autospec=True — patch ni haqiqiy imzoga mos qiladi: xato argument soni yoki nomi ushlanadi.
2.6. MagicMock — sehrli metodlar
Mock sehrli (__len__, __getitem__, __iter__) metodlarni qo'llamaydi; MagicMock — qo'llaydi:
from unittest.mock import MagicMock
m = MagicMock()
m.__len__.return_value = 5
len(m) # 5
m[0] # ishlaydi patch sukut bo'yicha MagicMock ishlatadi — shuning uchun ko'pincha alohida yaratish shart emas.
2.7. Bir necha patch
@patch("app.sms_yubor")
@patch("app.haroratni_ol") # eng yaqin — birinchi argument
def test_x(mock_harorat, mock_sms): # pastdan yuqoriga tartib
...Dekoratorlar pastdan yuqoriga argumentga aylanadi: eng yaqin dekorator — birinchi parametr.
2.8. Qachon patch, qachon in'eksiya
| Yondashuv | Qachon |
|---|---|
| In'eksiya (o'tgan dars) | Yangi kod — bog'liqlikni parametr qilib bering |
patch |
Mavjud kod — in'eksiya qilinmagan bog'liqlik |
Yangi kod yozayotganda bog'liqlikni in'eksiya qilish 13.5-bob mock qilishni osonlashtiradi va patch tuzoqlarini yo'q qiladi. patch — mavjud kodni o'zgartirmasdan test qilish uchun.
3. Tez ma'lumotnoma
from unittest.mock import patch, MagicMock
# kontekst menejeri
with patch("modul.funksiya", return_value=42) as m:
...
m.assert_called_once()
# dekorator (mock pastdan argument bo'lib keladi)
@patch("modul.funksiya", return_value=42)
def test_x(mock_f): ...
patch.object(obyekt, "atribut", return_value=...)
patch.dict(os.environ, {"K": "V"})
patch("modul.funksiya", autospec=True) # xavfsizQoidalar
QAYERDA ISHLATILSA, o'sha yerda patch qil (ta'riflangan joy emas)
from X import Y → patch("mening_modul.Y")
import X; X.Y() → patch("X.Y")
patch avtomatik tiklaydi (with/dekorator)
autospec=True — imzoni tekshirish
os.environ — patch.dict
yangi kod — in'eksiya, mavjud kod — patch4. Batafsil misollar
Misollarda
pytestni deterministik ishga tushiruvchipytest_ishgayordamchisi ishlatiladi.
Misol 1 — patch: kontekst menejeri va dekorator
"""patch tashqi chaqiruvni almashtiradi; kontekst menejeri va dekorator shakllari; mock chaqiruvini tekshirish; patch avtomatik tiklaydi."""
import re
import subprocess
import sys
import tempfile
from pathlib import Path
def pytest_ishga(katalog: Path, *args: str) -> tuple[int, list[str]]:
r = subprocess.run([sys.executable, "-m", "pytest", "-p", "no:cacheprovider", "-o", "addopts=", *args],
cwd=katalog, capture_output=True, text=True, encoding="utf-8")
satrlar = []
for l in r.stdout.splitlines():
if re.match(r"(=+ test session|collecting|collected|platform |cachedir|rootdir|plugins|configfile|hypothesis profile)", l):
continue
l = re.sub(r"\s+\[\s*\d+%\]$", "", l)
l = re.sub(r" in [\d.]+s\b", "", l)
l = re.sub(r"^(=+) (.*?) =+$", r"--- \2 ---", l)
satrlar.append(l.rstrip())
while satrlar and satrlar[0] == "":
satrlar.pop(0)
while satrlar and satrlar[-1] == "":
satrlar.pop()
return r.returncode, satrlar
KOD = '''
def haroratni_ol(shahar):
raise RuntimeError("haqiqiy tarmoq chaqiruvi — testda ishlamasligi kerak")
def xabar(shahar):
t = haroratni_ol(shahar)
holat = "issiq" if t > 25 else "sovuq"
return f"{shahar}: {t} daraja ({holat})"
'''
TEST = '''
from unittest.mock import patch
import ob_havo
def test_kontekst_menejer():
with patch("ob_havo.haroratni_ol", return_value=30) as mock_h:
assert ob_havo.xabar("Toshkent") == "Toshkent: 30 daraja (issiq)"
mock_h.assert_called_once_with("Toshkent")
def test_sovuq():
with patch("ob_havo.haroratni_ol", return_value=10):
assert ob_havo.xabar("Samarqand") == "Samarqand: 10 daraja (sovuq)"
@patch("ob_havo.haroratni_ol", return_value=26)
def test_dekorator(mock_h):
assert "issiq" in ob_havo.xabar("Buxoro")
assert mock_h.called
def test_patch_tiklandi():
# oldingi testlardagi patch tiklangan — haqiqiy funksiya qaytdi
import ob_havo
try:
ob_havo.haroratni_ol("X")
tiklandi = False
except RuntimeError:
tiklandi = True
assert tiklandi
def test_xato_holati():
with patch("ob_havo.haroratni_ol", side_effect=TimeoutError("vaqt tugadi")):
import pytest
with pytest.raises(TimeoutError):
ob_havo.xabar("Xiva")
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "ob_havo.py").write_text(KOD, encoding="utf-8")
(r / "test_ob_havo.py").write_text(TEST, encoding="utf-8")
print("=== 1. patch bilan testlar ===")
kod, satrlar = pytest_ishga(r, "-v", "--tb=short")
for satr in satrlar:
if "::" in satr or satr.startswith("---"):
print(f" {satr}")
print(f" chiqish kodi: {kod}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. patch bilan testlar ===
test_ob_havo.py::test_kontekst_menejer PASSED
test_ob_havo.py::test_sovuq PASSED
test_ob_havo.py::test_dekorator PASSED
test_ob_havo.py::test_patch_tiklandi PASSED
test_ob_havo.py::test_xato_holati PASSED
--- 5 passed ---
chiqish kodi: 0Nima ko'rsatdi: 2.1, 2.5-bo'limlar.
Misol 2 — Qayerda patch qilish kerak
"""patch 'qayerda ishlatilsa, o'sha yerda' qoidasi; from X import Y → app.Y ni patch qilish kerak, vaqt.Y ni emas; deterministik namoyish."""
import re
import subprocess
import sys
import tempfile
from pathlib import Path
def pytest_ishga(katalog: Path, *args: str) -> tuple[int, list[str]]:
r = subprocess.run([sys.executable, "-m", "pytest", "-p", "no:cacheprovider", "-o", "addopts=", *args],
cwd=katalog, capture_output=True, text=True, encoding="utf-8")
satrlar = []
for l in r.stdout.splitlines():
if re.match(r"(=+ test session|collecting|collected|platform |cachedir|rootdir|plugins|configfile|hypothesis profile)", l):
continue
l = re.sub(r"\s+\[\s*\d+%\]$", "", l)
l = re.sub(r" in [\d.]+s\b", "", l)
l = re.sub(r"^(=+) (.*?) =+$", r"--- \2 ---", l)
satrlar.append(l.rstrip())
while satrlar and satrlar[0] == "":
satrlar.pop(0)
while satrlar and satrlar[-1] == "":
satrlar.pop()
return r.returncode, satrlar
# manba modul — 'joriy soat'ni beruvchi (demo uchun deterministik: 15 qaytaradi)
MANBA = '''
def hozir_soat():
return 15 # demoda "joriy" soat — deterministik
'''
# 'from manba import hozir_soat' — nom 'app' fazosiga ko'chadi
APP = '''
from manba import hozir_soat
def salomlash():
return "tong" if hozir_soat() < 12 else "kun"
'''
TEST = '''
from unittest.mock import patch
import app
def test_notogri_joy_ishlamaydi():
# manba.hozir_soat ni patch qilamiz — LEKIN app allaqachon o'z nusxasini import qilgan
with patch("manba.hozir_soat", return_value=9):
# app.salomlash hali ham asl (15) qiymatni ishlatadi → "kun"
assert app.salomlash() == "kun" # patch ISHLAMADI
def test_togri_joy_ishlaydi():
# app.hozir_soat ni patch qilamiz — app aynan shuni ishlatadi
with patch("app.hozir_soat", return_value=9):
assert app.salomlash() == "tong" # patch ISHLADI
def test_patch_object_ham_togri():
with patch.object(app, "hozir_soat", return_value=9):
assert app.salomlash() == "tong"
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "manba.py").write_text(MANBA, encoding="utf-8")
(r / "app.py").write_text(APP, encoding="utf-8")
(r / "test_joy.py").write_text(TEST, encoding="utf-8")
print("=== 1. 'Qayerda patch qilish' testlari ===")
kod, satrlar = pytest_ishga(r, "-v", "--tb=short")
for satr in satrlar:
if "::" in satr or satr.startswith("---"):
print(f" {satr}")
print(f" chiqish kodi: {kod}")
print(" ⭐ manba.hozir_soat patch → app ta'sirlanmaydi (o'z nusxasi bor)")
print(" ⭐ app.hozir_soat patch → ishlaydi (app aynan shuni chaqiradi)")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. 'Qayerda patch qilish' testlari ===
test_joy.py::test_notogri_joy_ishlamaydi PASSED
test_joy.py::test_togri_joy_ishlaydi PASSED
test_joy.py::test_patch_object_ham_togri PASSED
--- 3 passed ---
chiqish kodi: 0
⭐ manba.hozir_soat patch → app ta'sirlanmaydi (o'z nusxasi bor)
⭐ app.hozir_soat patch → ishlaydi (app aynan shuni chaqiradi)Nima ko'rsatdi: 2.2, 2.3-bo'limlar.
Misol 3 — patch.dict, autospec va MagicMock
"""patch.dict bilan os.environ; autospec noto'g'ri imzoni ushlaydi; MagicMock sehrli metodlar (__len__, __getitem__); patch sukut MagicMock ishlatadi."""
import re
import subprocess
import sys
import tempfile
from pathlib import Path
def pytest_ishga(katalog: Path, *args: str) -> tuple[int, list[str]]:
r = subprocess.run([sys.executable, "-m", "pytest", "-p", "no:cacheprovider", "-o", "addopts=", *args],
cwd=katalog, capture_output=True, text=True, encoding="utf-8")
satrlar = []
for l in r.stdout.splitlines():
if re.match(r"(=+ test session|collecting|collected|platform |cachedir|rootdir|plugins|configfile|hypothesis profile)", l):
continue
l = re.sub(r"\s+\[\s*\d+%\]$", "", l)
l = re.sub(r" in [\d.]+s\b", "", l)
l = re.sub(r"^(=+) (.*?) =+$", r"--- \2 ---", l)
satrlar.append(l.rstrip())
while satrlar and satrlar[0] == "":
satrlar.pop(0)
while satrlar and satrlar[-1] == "":
satrlar.pop()
return r.returncode, satrlar
KOD = '''
import os
def rejim():
return os.environ.get("WISAR_REJIM", "ishlab_chiqarish")
def hisobla(a, b):
return a + b
'''
TEST = '''
from unittest.mock import patch, MagicMock, Mock
import app
def test_patch_dict_env():
with patch.dict("os.environ", {"WISAR_REJIM": "test"}):
assert app.rejim() == "test"
def test_env_tiklandi():
assert app.rejim() == "ishlab_chiqarish"
def test_autospec_notogri_imzo():
with patch("app.hisobla", autospec=True) as m:
m.return_value = 5
app.hisobla(2, 3) # to'g'ri imzo
m.assert_called_once_with(2, 3)
# autospec bilan mock asl imzoga bog'langan
def test_magicmock_sehrli():
m = MagicMock()
m.__len__.return_value = 3
m.__getitem__.return_value = "element"
assert len(m) == 3
assert m[0] == "element"
def test_oddiy_mock_sehrsiz():
m = Mock()
import pytest
with pytest.raises(TypeError):
len(m) # Mock __len__ ni qo'llamaydi
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "app.py").write_text(KOD, encoding="utf-8")
(r / "test_app.py").write_text(TEST, encoding="utf-8")
print("=== 1. patch.dict, autospec, MagicMock ===")
kod, satrlar = pytest_ishga(r, "-v", "--tb=short")
for satr in satrlar:
if "::" in satr or satr.startswith("---"):
print(f" {satr}")
print(f" chiqish kodi: {kod}")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. patch.dict, autospec, MagicMock ===
test_app.py::test_patch_dict_env PASSED
test_app.py::test_env_tiklandi PASSED
test_app.py::test_autospec_notogri_imzo PASSED
test_app.py::test_magicmock_sehrli PASSED
test_app.py::test_oddiy_mock_sehrsiz PASSED
--- 5 passed ---
chiqish kodi: 0Nima ko'rsatdi: 2.4, 2.5, 2.6-bo'limlar.
Misol 4 — Amaliy: tarmoq chaqiruvli modulni test qilish
Valyuta kursini API dan olib, so'mga aylantiruvchi modulni test qilamiz. Modul urllib bilan haqiqiy so'rov yuboradi — bu in'eksiya qilinmagan, mavjud kod. patch bilan tarmoq chaqiruvini almashtirib, muvaffaqiyat, xato javob va tarmoq nosozligi holatlarini izolyatsiyada tekshiramiz — haqiqiy internetsiz, tez va barqaror.
"""mavjud kodni (urllib bilan) patch qilib test qilish: muvaffaqiyatli javob, xato status, tarmoq nosozligi (side_effect); chaqiruvni tekshirish; keshni tekshirish."""
import re
import subprocess
import sys
import tempfile
from pathlib import Path
KOD = '''
import json
import urllib.request
class ValyutaXatosi(Exception):
pass
def kursni_ol(valyuta):
"""API dan valyuta kursini oladi (1 birlik necha so'm)."""
try:
javob = urllib.request.urlopen(f"https://api.example/rate?c={valyuta}", timeout=5)
except OSError as e:
raise ValyutaXatosi(f"tarmoq xatosi: {e}") from e
malumot = json.loads(javob.read())
if "rate" not in malumot:
raise ValyutaXatosi(f"noma'lum valyuta: {valyuta}")
return malumot["rate"]
def somga_aylantir(summa, valyuta):
kurs = kursni_ol(valyuta)
return round(summa * kurs, 2)
'''
TEST = '''
from unittest.mock import patch, MagicMock
import pytest
import valyuta
def soxta_javob(malumot: bytes):
m = MagicMock()
m.read.return_value = malumot
return m
def test_muvaffaqiyatli_aylantirish():
with patch("urllib.request.urlopen", return_value=soxta_javob(b'{"rate": 12650}')) as m:
natija = valyuta.somga_aylantir(100, "USD")
assert natija == 1265000.0
assert m.called
def test_kurs_ol():
with patch("urllib.request.urlopen", return_value=soxta_javob(b'{"rate": 13800}')):
assert valyuta.kursni_ol("EUR") == 13800
def test_nomalum_valyuta():
with patch("urllib.request.urlopen", return_value=soxta_javob(b'{"error": "not found"}')):
with pytest.raises(valyuta.ValyutaXatosi, match="noma'lum valyuta"):
valyuta.kursni_ol("XXX")
def test_tarmoq_nosozligi():
with patch("urllib.request.urlopen", side_effect=OSError("ulanish yo'q")):
with pytest.raises(valyuta.ValyutaXatosi, match="tarmoq xatosi"):
valyuta.kursni_ol("USD")
def test_togri_url_chaqirilgan():
with patch("urllib.request.urlopen", return_value=soxta_javob(b'{"rate": 100}')) as m:
valyuta.kursni_ol("RUB")
chaqiruv_url = m.call_args[0][0]
assert "c=RUB" in chaqiruv_url
@patch("urllib.request.urlopen")
def test_dekorator_shakli(mock_urlopen):
mock_urlopen.return_value = soxta_javob(b'{"rate": 12000}')
assert valyuta.somga_aylantir(50, "USD") == 600000.0
'''
def pytest_ishga(katalog: Path, *args: str) -> tuple[int, list[str]]:
r = subprocess.run([sys.executable, "-m", "pytest", "-p", "no:cacheprovider", "-o", "addopts=", *args],
cwd=katalog, capture_output=True, text=True, encoding="utf-8")
satrlar = []
for l in r.stdout.splitlines():
if re.match(r"(=+ test session|collecting|collected|platform |cachedir|rootdir|plugins|configfile|hypothesis profile)", l):
continue
l = re.sub(r"\s+\[\s*\d+%\]$", "", l)
l = re.sub(r" in [\d.]+s\b", "", l)
l = re.sub(r"^(=+) (.*?) =+$", r"--- \2 ---", l)
satrlar.append(l.rstrip())
while satrlar and satrlar[0] == "":
satrlar.pop(0)
while satrlar and satrlar[-1] == "":
satrlar.pop()
return r.returncode, satrlar
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "valyuta.py").write_text(KOD, encoding="utf-8")
(r / "test_valyuta.py").write_text(TEST, encoding="utf-8")
print("=== 1. Tarmoq chaqiruvli modul (patch bilan) ===")
kod, satrlar = pytest_ishga(r, "-v", "--tb=short")
for satr in satrlar:
if "::" in satr or satr.startswith("---"):
print(f" {satr}")
print(f"\n chiqish kodi: {kod}")
print(" ⭐ haqiqiy internetsiz: muvaffaqiyat, xato, tarmoq nosozligi tekshirildi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Tarmoq chaqiruvli modul (patch bilan) ===
test_valyuta.py::test_muvaffaqiyatli_aylantirish PASSED
test_valyuta.py::test_kurs_ol PASSED
test_valyuta.py::test_nomalum_valyuta PASSED
test_valyuta.py::test_tarmoq_nosozligi PASSED
test_valyuta.py::test_togri_url_chaqirilgan PASSED
test_valyuta.py::test_dekorator_shakli PASSED
--- 6 passed ---
chiqish kodi: 0
⭐ haqiqiy internetsiz: muvaffaqiyat, xato, tarmoq nosozligi tekshirildiNima ko'rsatdi: 2.1, 2.5, 2.6-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Obyekt ta'riflangan joyda patch qilinadi" | Ishlatiladigan joyda |
"from X import Y da X.Y ni patch qilaman" |
mening_modul.Y ni |
| "Patch ni qo'lda tiklash kerak" | with/dekorator avtomatik tiklaydi |
"patch mock ni spec bilan yaratadi" |
Yo'q — autospec=True kerak |
"Mock len() ni qo'llaydi" |
MagicMock qo'llaydi |
| "Dekoratorlar yuqoridan pastga argument" | Pastdan yuqoriga |
"os.environ ni to'g'ridan o'zgartirsam bo'ladi" |
patch.dict — avtomatik tiklaydi |
| "Patch — yagona usul" | In'eksiya (yangi kod) — afzalroq |
6. Keng tarqalgan xatolar va yechimlari
1. Noto'g'ri joyni patch qilish
# app.py: from api import olish
patch("api.olish", ...) # ❌ app ta'sirlanmaydi
patch("app.olish", ...) # ✅2. Patch ni tiklamaslik (qo'lda)
app.olish = Mock() # ❌ keyingi testga ta'sir
with patch("app.olish"): ... # ✅ avtomatik tiklaydi3. autospec siz noto'g'ri imzo
with patch("app.tolov"): # ❌ noto'g'ri arg jim o'tadi
with patch("app.tolov", autospec=True): # ✅ imzo tekshiriladi4. Dekorator argument tartibi
@patch("app.a")
@patch("app.b")
def test(mock_a, mock_b): ... # ❌ tartib teskari
def test(mock_b, mock_a): ... # ✅ pastdan yuqoriga5. os.environ ni to'g'ridan-to'g'ri
os.environ["X"] = "1" # ❌ tiklanmaydi
with patch.dict("os.environ", {"X": "1"}): ... # ✅6. len/[] uchun Mock
m = Mock(); len(m) # ❌ TypeError
m = MagicMock(); m.__len__.return_value = 3 # ✅7. Patch yo'lini noto'g'ri yozish
patch("olish", ...) # ❌ to'liq yo'l kerak
patch("mening_paket.mening_modul.olish", ...) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 17.5-dars (o'tilgan): mock tushunchasi —
patchmock yaratadi - 17.3-dars (o'tilgan):
monkeypatch—pytestning patch varianti - 17.9-dars: integratsion testlar — qachon patch, qachon haqiqiy
- 20.15-dars: FastAPI — tashqi bog'liqliklarni patch qilish
- 26.3–26.7-darslar: scraping va API — tarmoqni patch bilan test
8. Eng yaxshi amaliyotlar
"Qayerda ishlatilsa, o'sha yerda patch qil" — ta'riflangan joyni emas.
with/dekorator ishlating — patch avtomatik tiklanadi.autospec=Truebilan imzoni tekshiring.os.environva lug'atlar uchunpatch.dict.Sehrli metodlar kerak bo'lsa
MagicMock.Bir necha
@patchda argument tartibiga (pastdan yuqoriga) e'tibor bering.Yangi kod yozayotganda bog'liqlikni in'eksiya qiling —
patchdan afzal.Patch yo'lini to'liq va aniq yozing.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
# app.py: from vaqt import soat deb faraz qiling. Har patch ishlaydimi (ISHLAYDI/ISHLAMAYDI)?
1. patch("app.soat", return_value=9)
2. patch("vaqt.soat", return_value=9) # app 'from vaqt import soat' qilgan
3. patch.object(app, "soat", return_value=9)Va quyidagilar:
from unittest.mock import Mock, MagicMock, patch
4. m = MagicMock(); m.__len__.return_value = 2; print(len(m))
5. m = Mock()
try: len(m); print("ok")
except TypeError: print("xato")
6. with patch.dict("os.environ", {"X": "1"}):
print(os.environ["X"])
7. import os
print("X" in os.environ) # 6-blokdan keyin
8. @patch("app.b")
@patch("app.a")
def test(m1, m2): print(m1._mock_name, m2._mock_name) # qaysi qaysi
9. with patch("app.f", return_value=5) as m:
app.f(); print(m.call_count)
10. m = MagicMock(); print(bool(m))
11. m = Mock(return_value=None); print(m() is None)
12. with patch("app.f") as m:
print(type(m).__name__)Javoblar
ISHLAYDI— app ishlatadigan nusxaISHLAMAYDI— app o'z nusxasini import qilganISHLAYDI— app.soat ni almashtiradi2xato—Mock__len__ni qo'llamaydi1False—patch.dicttikladia b— eng yaqin dekorator birinchi argument (m1=app.a)1True—MagicMocksukut rostTrueMagicMock—patchsukut MagicMock
Vazifa 2: Xatolarni tuzating
1. # app.py: from requests import get
def test_x():
with patch("requests.get", return_value=soxta):
assert app.olish() == kutilgan
2. def test_env():
os.environ["REJIM"] = "test"
assert app.rejim() == "test"
3. def test_len():
m = Mock()
m.__len__ = lambda: 5 # Mock sehrli metod
assert len(m) == 5
4. @patch("app.sms")
@patch("app.email")
def test_xabar(mock_sms, mock_email):
app.xabar_yubor()
5. def test_tolov():
with patch("app.tolov_qil"): # noto'g'ri imzo jim o'tadi
app.buyurtma()Javoblar
1. # from requests import get → app.get ni patch qilish
def test_x():
with patch("app.get", return_value=soxta):
assert app.olish() == kutilgan
2. def test_env():
with patch.dict("os.environ", {"REJIM": "test"}):
assert app.rejim() == "test"
3. def test_len():
m = MagicMock() # MagicMock
m.__len__.return_value = 5
assert len(m) == 5
4. @patch("app.sms") # pastdan yuqoriga argument
@patch("app.email")
def test_xabar(mock_email, mock_sms):
app.xabar_yubor()
5. def test_tolov():
with patch("app.tolov_qil", autospec=True): # imzo tekshiriladi
app.buyurtma()Vazifa 3: Email yuboruvchini test qilish
smtplib bilan email yuboradigan modulni:
patchbilansmtplib.SMTPni almashtiring- Muvaffaqiyatli yuborish va SMTP xatosini test qiling
- To'g'ri manzil va mavzu bilan chaqirilganini tasdiqlang
autospecbilan xavfsizroq qiling
Vazifa 4: Vaqtni patch qilish
datetime.now() ishlatadigan funksiyani:
patchbilan aniq vaqt qaytaring (freeze)- Turli sanalar uchun mantiqni test qiling (dam olish kuni, bayram)
- "Qayerda ishlatilsa" qoidasini to'g'ri qo'llang
freezegunkutubxonasi bilan solishtiring (ixtiyoriy)
Vazifa 5: Fayl tizimini patch qilish
Fayl o'qiydigan funksiyani:
patchbilanopennimock_openyordamida almashtiring- Fayl mazmuni, yo'q fayl, ruxsat xatosi holatlari
pathlib.Path.read_textni ham patch qiling- Haqiqiy
tmp_path17.3-bob bilan solishtiring — qaysi biri afzal
Vazifa 6: Retry va timeout
Qayta urinuvchi tarmoq funksiyasini:
side_effectro'yxati bilan "ikki xato, keyin muvaffaqiyat"call_countbilan urinishlar sonini tasdiqlang- Barcha urinish yiqilsa xato tarqalishini
autospecbilan API imzosini himoya qiling
Vazifa 7: O'ylash
"Qayerda ishlatilsa, o'sha yerda patch qil" qoidasi mock ning eng ko'p adashtiigan jihati. Bu qoida Python import tizimining ishlash tarzidan kelib chiqadi. Nima uchun from modul import nom va import modul patch qilishda farq qiladi, va bu farq Python nomlar fazolari haqida nimani o'rgatadi?
Javob
Qisqa javob: from modul import nom nom ni joriy modulning nomlar fazosiga nusxalaydi — endi ikki joyda ikki nom bir obyektga ishora qiladi. patch bitta nomni almashtirsa, ikkinchisi eski obyektga ishora qilishda davom etadi. import modul esa modul nomini oladi va modul.nom har safar modul ichidan qidiriladi — shuning uchun modul.nom ni patch qilish ishlaydi. Bu Python'ning "nom — obyektga bog'lanish" tabiatini ochib beradi.
1. from modul import nom nima qiladi
# app.py
from vaqt import soat # app.soat = vaqt.soat (nusxa bog'lanish)Endi app.soat va vaqt.soat — ikki alohida nom, bir obyektga ishora qiladi. patch("vaqt.soat") faqat vaqt.soat ni almashtiradi; app.soat eski obyektga bog'langan qoladi.
2. import modul nima qiladi
# app.py
import vaqt
def salom():
return vaqt.soat() # har safar vaqt.soat qidiriladiBu yerda app faqat vaqt nomini oladi; vaqt.soat ishlatilganda qidiriladi. patch("vaqt.soat") ishlaydi, chunki qidirish patch qilingan joyga boradi.
3. Umumiy qoida
| Import uslubi | Patch qilinadigan joy |
|---|---|
from X import Y (keyin Y()) |
mening_modul.Y |
import X (keyin X.Y()) |
X.Y |
4. Python nomlar fazosi darsi
| Tushuncha | Ma'nosi |
|---|---|
| Nom — bog'lanish | O'zgaruvchi obyekt emas, unga ishora |
from import — nusxa |
Yangi nom, bir obyekt |
| Modul — nomlar fazosi | Har modulning o'z jadvali |
| Kechiktirilgan qidirish | X.Y ishlatilganda qidiriladi |
5. Amaliy xulosa
- Test yozishni osonlashtirish uchun
import moduluslubi (patch moslashuvchan) - Yoki bog'liqlikni in'eksiya qiling — patch tuzog'i umuman yo'q
- "Qayerda ishlatilsa" — nom qayerdan qidirilishiga bog'liq
- Bu Python nomlar fazolari va bog'lanishlarining tabiiy natijasi
6. Xulosa
from importnomni nusxalaydi — patch ishlatilgan joydaimport modulkechiktirilgan qidirish — patch manba joyda- Bu Python "nom — bog'lanish" modelidan kelib chiqadi
- In'eksiya bu murakkablikni umuman yo'q qiladi
Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.
Xulosa
Bu darsda patch bilan mavjud koddagi bog'liqlikni almashtirishni o'rgandik.
Eng muhim uch fikr:
patchmavjud obyektni vaqtincha almashtiradi va avtomatik tiklaydi. Kontekst menejeri (with patch(...)) yoki dekorator (@patch(...)) shaklida ishlaydi; dekoratorda mock pastdan yuqoriga argument bo'lib keladi. In'eksiya qilinmagan bog'liqlik (to'g'ridan-to'g'rirequests.get,datetime.now,open) uchun — in'eksiyadan farqli o'laroq — mavjud kodni o'zgartirmasdan test qilish yo'li."Qayerda ishlatilsa, o'sha yerda patch qil." Bu — mock ning eng muhim va eng ko'p adashtiruvchi qoidasi.
from modul import nomnomni joriy modul fazosiga nusxalaydi, shuning uchunpatch("mening_modul.nom")kerak,patch("modul.nom")emas.import modulbo'lsapatch("modul.nom")to'g'ri. Bu Python nomlar fazosining tabiatidan kelib chiqadi.patch.object,patch.dict,autospec,MagicMock.patch.object— obyekt va atribut nomi bilan;patch.dict— lug'at (masalanos.environ) ni vaqtincha o'zgartirib avtomatik tiklaydi;autospec=Truemock ni asl imzoga bog'lab noto'g'ri chaqiruvni ushlaydi;MagicMocksehrli metodlarni (__len__,__getitem__) qo'llaydi (patchsukut bo'yicha uni ishlatadi). Va eng yaxshi amaliyot: yangi kod yozayotganda bog'liqlikni in'eksiya qiling — bupatchtuzoqlarini umuman yo'q qiladi.
Keyingi darsda coverage ni o'rganamiz: testlar kodning qancha qismini qamrab olganini o'lchash, qaysi qatorlar test qilinmaganini topish va qamrov metrikasini to'g'ri (va noto'g'ri) talqin qilish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!