Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Fixture nima
- 2.2. Fixture nima uchun
- 2.3. yield fixture — setup va teardown
- 2.4. Ko'lam (scope)
- 2.5. Fixture kompozitsiyasi
- 2.6. O'rnatilgan (built-in) fixture lar
- 2.7. conftest.py — fixture larni ulashish
- 2.8. autouse — avtomatik fixture
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Asosiy fixture va kompozitsiya
- Misol 2 — yield fixture va scope
- Misol 3 — O'rnatilgan fixture lar: tmp_path, monkeypatch, capsys
- Misol 4 — Amaliy: conftest.py va baza fixture
- 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.3-dars: Fixture
17-QISM — TESTLASH · 3-dars
1. Kirish va motivatsiya
O'tgan darslardagi testlarda bir muammo bor edi: har testda bir xil tayyorlash kodini takrorladik. Har savat testida s = Savat(), har parol testida bir xil test ma'lumoti. Bu takror — kodning dushmani. Yechim — fixture: testga kerakli narsani (obyekt, ma'lumot, ulanish, vaqtinchalik fayl) tayyorlab beruvchi funksiya.
Fixture — pytest ning eng kuchli va o'ziga xos xususiyati. U testni toza (faqat tekshiruv mantiqi), mustaqil (har test o'z yangi ma'lumotini oladi) va oson o'qiladigan qiladi.
Real vaziyat. Bir jamoa baza bilan ishlaydigan kodni test qilardi. Har testda: bazaga ulanish, jadval yaratish, sinov ma'lumotini yozish, test bajarish, keyin tozalash. Bu 10 qator kod har testda takrorlanardi — 50 test = 500 qator takror. Bittasida tozalash unutildi va testlar bir-biriga ta'sir qila boshladi: bitta test yozgan ma'lumot boshqasini buzardi. Ular fixture ga o'tishdi: ulanish va tozalash bir marta fixture da yozildi, har test uni parametr sifatida so'rab, avtomatik yangi, toza bazaga ega bo'ldi.
Bu darsda fixture larni — testga kerak narsani tayyorlab, keyin tozalab beruvchi mexanizmni o'rganamiz.
Bu darsda:
-
@pytest.fixture— nima va nega - Fixture ni parametr sifatida so'rash
-
yieldfixture — tayyorlash va tozalash (setup/teardown) - Ko'lam (scope): function, class, module, session
- Fixture fixture ni ishlatishi (kompozitsiya)
- O'rnatilgan fixture lar:
tmp_path,monkeypatch,capsys conftest.py— fixture larni ulashish- Amaliy: baza fixture bilan repozitoriyni test qilish
2. Nazariya — chuqur tushuntirish
2.1. Fixture nima
Fixture — @pytest.fixture bilan belgilangan funksiya. U testga kerakli narsani tayyorlab qaytaradi. Test uni parametr nomi orqali so'raydi:
import pytest
@pytest.fixture
def sonlar():
return [1, 2, 3, 4, 5]
def test_yigindi(sonlar): # pytest 'sonlar' fixture ni topib chaqiradi
assert sum(sonlar) == 15
def test_uzunlik(sonlar): # har test o'z YANGI ro'yxatini oladi
assert len(sonlar) == 5 pytest fixture nomini test parametrida ko'rib, uni avtomatik chaqiradi va natijani beradi. Bu — dependency injection (13.5, 20.7).
2.2. Fixture nima uchun
| Foyda | Tafsilot |
|---|---|
| Takrorni yo'q qiladi | Tayyorlash bir marta yoziladi |
| Mustaqillik | Har test yangi, toza nusxa oladi |
| Toza test | Test faqat tekshiruv mantiqiga e'tibor beradi |
| Tozalash | yield bilan avtomatik teardown |
| Kompozitsiya | Fixture boshqa fixture ni ishlatadi |
2.3. yield fixture — setup va teardown
Resurs (fayl, ulanish, server) ishlatilgach tozalanishi kerak bo'lsa, return o'rniga yield:
@pytest.fixture
def baza():
con = ulanish_och() # SETUP — testdan oldin
yield con # test shu qiymatni oladi
con.close() # TEARDOWN — testdan keyin (test yiqilsa ham) yield gacha — tayyorlash, yield dan keyin — tozalash. Tozalash test yiqilsa ham bajariladi (xuddi try/finally kabi).
2.4. Ko'lam (scope)
Fixture qancha marta chaqirilishini scope belgilaydi:
| Scope | Chaqiriladi | Qachon |
|---|---|---|
function (sukut) |
Har test uchun | Mustaqillik kerak |
class |
Har sinf uchun bir marta | Sinf testlari umumiy |
module |
Har fayl uchun bir marta | Qimmat tayyorlash |
session |
Butun ishga tushirishda bir marta | Juda qimmat (baza serveri) |
@pytest.fixture(scope="module")
def qimmat_resurs():
return qimmat_hisob() # fayl bo'yicha bir marta Kengroq scope tezroq, lekin testlar umumiy holatni bo'lishadi — o'zgartiriladigan ma'lumotda ehtiyot bo'ling. Sukut function — eng xavfsiz.
2.5. Fixture kompozitsiyasi
Fixture boshqa fixture ni parametr sifatida so'rashi mumkin:
@pytest.fixture
def bosh_savat():
return Savat()
@pytest.fixture
def toldirilgan_savat(bosh_savat): # bosh_savat fixture ni ishlatadi
bosh_savat.qosh("kitob", 45000)
return bosh_savatBu murakkab tayyorlashni kichik, qayta ishlatiladigan qismlarga bo'ladi.
2.6. O'rnatilgan (built-in) fixture lar
pytest tayyor fixture lar beradi:
| Fixture | Nima beradi |
|---|---|
tmp_path |
Vaqtinchalik Path papka (har test uchun yangi) |
monkeypatch |
Vaqtincha o'zgartirish (env, atribut) — testdan keyin tiklanadi |
capsys |
print/stdout ni ushlash |
caplog |
Jurnal (logging) yozuvlarini ushlash |
tmp_path_factory |
Session bo'yicha vaqtinchalik papka |
def test_fayl(tmp_path):
fayl = tmp_path / "test.txt"
fayl.write_text("salom")
assert fayl.read_text() == "salom"
def test_muhit(monkeypatch):
monkeypatch.setenv("REJIM", "test") # test tugagach avtomatik tiklanadi
assert os.environ["REJIM"] == "test"
def test_chiqish(capsys):
print("salom")
assert capsys.readouterr().out == "salom\n" monkeypatch — tashqi bog'liqlikni (vaqt, env, funksiya) vaqtincha almashtirishning xavfsiz yo'li; keyingi darsda (mock) chuqurroq.
2.7. conftest.py — fixture larni ulashish
Bir necha test faylida ishlatiladigan fixture ni conftest.py fayliga qo'ying — pytest uni avtomatik topadi, import shart emas:
tests/
conftest.py # umumiy fixture lar
test_savat.py # conftest fixture larini ishlatadi
test_tolov.py conftest.py — o'sha papka va ost-papkalaridagi barcha testga ko'rinadi.
2.8. autouse — avtomatik fixture
autouse=True fixture ni so'ramasdan har testga qo'llaydi (masalan har testdan oldin holatni tiklash):
@pytest.fixture(autouse=True)
def har_testdan_oldin():
tozala_holat()
yieldEhtiyotkorlik bilan: yashirin xatti-harakat testni tushunishni qiyinlashtiradi.
3. Tez ma'lumotnoma
import pytest
@pytest.fixture
def malumot():
return {"nom": "test"}
@pytest.fixture
def baza():
con = och()
yield con # setup / teardown
con.close()
@pytest.fixture(scope="module")
def qimmat(): ...
def test_x(malumot, baza, tmp_path, monkeypatch, capsys):
...Qoidalar
takroriy tayyorlash — fixture ga
resurs (fayl/ulanish) — yield fixture, teardown
sukut scope function — mustaqillik uchun
umumiy fixture — conftest.py
vaqtinchalik fayl — tmp_path
env/atribut vaqtincha — monkeypatch
chiqishni tekshirish — capsys4. Batafsil misollar
Misollarda
pytestni deterministik ishga tushiruvchipytest_ishgayordamchisi ishlatiladi.
Misol 1 — Asosiy fixture va kompozitsiya
"""fixture ni parametr sifatida so'rash; har test yangi nusxa oladi (mustaqillik); fixture fixture ni ishlatadi (kompozitsiya)."""
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 = '''
class Savat:
def __init__(self):
self.mahsulotlar = []
def qosh(self, nom):
self.mahsulotlar.append(nom)
'''
TEST = '''
import pytest
from savat import Savat
@pytest.fixture
def sonlar():
return [1, 2, 3, 4, 5]
@pytest.fixture
def bosh_savat():
return Savat()
@pytest.fixture
def toldirilgan_savat(bosh_savat): # fixture kompozitsiyasi
bosh_savat.qosh("kitob")
bosh_savat.qosh("ruchka")
return bosh_savat
def test_yigindi(sonlar):
assert sum(sonlar) == 15
def test_royxat_ozgartirsam(sonlar):
sonlar.append(6)
assert len(sonlar) == 6
def test_boshqa_test_toza_royxat_oladi(sonlar):
# oldingi test 6 qo'shdi, lekin bu test YANGI ro'yxat oladi
assert len(sonlar) == 5
def test_bosh_savat(bosh_savat):
assert len(bosh_savat.mahsulotlar) == 0
def test_toldirilgan_savat(toldirilgan_savat):
assert len(toldirilgan_savat.mahsulotlar) == 2
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "savat.py").write_text(KOD, encoding="utf-8")
(r / "test_savat.py").write_text(TEST, encoding="utf-8")
print("=== 1. Fixture 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}")
print(" ⭐ test_boshqa_test_toza — mustaqillik: har test yangi ro'yxat oladi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Fixture bilan testlar ===
test_savat.py::test_yigindi PASSED
test_savat.py::test_royxat_ozgartirsam PASSED
test_savat.py::test_boshqa_test_toza_royxat_oladi PASSED
test_savat.py::test_bosh_savat PASSED
test_savat.py::test_toldirilgan_savat PASSED
--- 5 passed ---
chiqish kodi: 0
⭐ test_boshqa_test_toza — mustaqillik: har test yangi ro'yxat oladiNima ko'rsatdi: 2.1, 2.2, 2.5-bo'limlar.
Misol 2 — yield fixture va scope
"""yield fixture: setup va teardown (test yiqilsa ham teardown bajariladi); tozalash tartibi; scope=module bilan bir marta chaqirish."""
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
TEST = '''
import pytest
HODISALAR = []
@pytest.fixture
def resurs():
HODISALAR.append("SETUP")
yield "resurs-obyekti"
HODISALAR.append("TEARDOWN")
def test_resursdan_foydalanish(resurs):
HODISALAR.append("TEST ishladi")
assert resurs == "resurs-obyekti"
def test_tozalash_tartibini_korish():
# oldingi test tugagach teardown bajarilgan bo'lishi kerak
assert HODISALAR == ["SETUP", "TEST ishladi", "TEARDOWN"]
# scope=module — butun fayl uchun bir marta
CHAQIRUVLAR = []
@pytest.fixture(scope="module")
def qimmat_resurs():
CHAQIRUVLAR.append("hisoblandi")
return {"tayyor": True}
def test_scope_a(qimmat_resurs):
assert qimmat_resurs["tayyor"]
def test_scope_b(qimmat_resurs):
assert qimmat_resurs["tayyor"]
def test_scope_bir_marta_chaqirilgan(qimmat_resurs):
assert len(CHAQIRUVLAR) == 1 # 3 test, lekin 1 marta hisoblandi
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "test_yield.py").write_text(TEST, encoding="utf-8")
print("=== 1. yield fixture: setup/teardown tartibi ===")
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(" ⭐ SETUP → TEST → TEARDOWN tartibi va scope=module bir marta chaqiruv tasdiqlandi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. yield fixture: setup/teardown tartibi ===
test_yield.py::test_resursdan_foydalanish PASSED
test_yield.py::test_tozalash_tartibini_korish PASSED
test_yield.py::test_scope_a PASSED
test_yield.py::test_scope_b PASSED
test_yield.py::test_scope_bir_marta_chaqirilgan PASSED
--- 5 passed ---
chiqish kodi: 0
⭐ SETUP → TEST → TEARDOWN tartibi va scope=module bir marta chaqiruv tasdiqlandiNima ko'rsatdi: 2.3, 2.4-bo'limlar.
Misol 3 — O'rnatilgan fixture lar: tmp_path, monkeypatch, capsys
"""tmp_path — vaqtinchalik fayl; monkeypatch — env va atributni vaqtincha almashtirish; capsys — chiqishni ushlash; hammasi izolyatsiyalangan."""
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 sozlamani_oqi():
return os.environ.get("WISAR_REJIM", "ishlab_chiqarish")
def hisobotni_yoz(yol, matn):
yol.write_text(matn, encoding="utf-8")
return yol.stat().st_size
def salomlash(ism):
print(f"Salom, {ism}!")
'''
TEST = '''
from app import sozlamani_oqi, hisobotni_yoz, salomlash
def test_vaqtinchalik_fayl(tmp_path):
fayl = tmp_path / "hisobot.txt"
hajm = hisobotni_yoz(fayl, "Wisar hisoboti")
assert fayl.exists()
assert fayl.read_text(encoding="utf-8") == "Wisar hisoboti"
assert hajm > 0
def test_har_test_yangi_papka(tmp_path):
# bu papka oldingi testnikidan boshqa — izolyatsiya
assert not (tmp_path / "hisobot.txt").exists()
def test_sukut_rejim():
assert sozlamani_oqi() == "ishlab_chiqarish"
def test_monkeypatch_env(monkeypatch):
monkeypatch.setenv("WISAR_REJIM", "test")
assert sozlamani_oqi() == "test"
def test_env_tiklandi():
# monkeypatch test tugagach o'zgarishni bekor qildi
assert sozlamani_oqi() == "ishlab_chiqarish"
def test_chiqishni_ushlash(capsys):
salomlash("Aziz")
natija = capsys.readouterr()
assert natija.out == "Salom, Aziz!\\n"
'''
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. O'rnatilgan fixture lar ===")
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(" ⭐ tmp_path izolyatsiya, monkeypatch env ni test tugagach tikladi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. O'rnatilgan fixture lar ===
test_app.py::test_vaqtinchalik_fayl PASSED
test_app.py::test_har_test_yangi_papka PASSED
test_app.py::test_sukut_rejim PASSED
test_app.py::test_monkeypatch_env PASSED
test_app.py::test_env_tiklandi PASSED
test_app.py::test_chiqishni_ushlash PASSED
--- 6 passed ---
chiqish kodi: 0
⭐ tmp_path izolyatsiya, monkeypatch env ni test tugagach tikladiNima ko'rsatdi: 2.6-bo'lim.
Misol 4 — Amaliy: conftest.py va baza fixture
Foydalanuvchi repozitoriyni (SQLite, 16.7) test qilamiz. Har testga toza, sinov ma'lumoti bilan to'ldirilgan baza kerak. Bazani ochish, sxema yaratish va tozalash — conftest.py dagi yield fixture da bir marta yoziladi; har test uni parametr sifatida so'rab, o'z izolyatsiyalangan bazasini oladi. Bu — baza kodini test qilishning standart naqshi.
"""conftest.py da baza fixture (yield bilan setup/teardown); har test toza baza oladi; fixture kompozitsiyasi (to'ldirilgan baza); repozitoriyni test qilish."""
import re
import subprocess
import sys
import tempfile
from pathlib import Path
KOD = '''
import sqlite3
class FoydalanuvchiRepo:
def __init__(self, con):
self.con = con
con.execute("CREATE TABLE IF NOT EXISTS foydalanuvchi(id INTEGER PRIMARY KEY, login TEXT UNIQUE, faol INTEGER DEFAULT 1)")
def qosh(self, login):
cur = self.con.execute("INSERT INTO foydalanuvchi(login) VALUES(?)", (login,))
return cur.lastrowid
def ol(self, login):
r = self.con.execute("SELECT login, faol FROM foydalanuvchi WHERE login=?", (login,)).fetchone()
return dict(zip(("login", "faol"), r)) if r else None
def faolsizlantir(self, login):
cur = self.con.execute("UPDATE foydalanuvchi SET faol=0 WHERE login=?", (login,))
return cur.rowcount > 0
def soni(self):
return self.con.execute("SELECT COUNT(*) FROM foydalanuvchi").fetchone()[0]
'''
CONFTEST = '''
import sqlite3
import pytest
from repo import FoydalanuvchiRepo
@pytest.fixture
def baza():
con = sqlite3.connect(":memory:") # SETUP: toza xotira bazasi
yield con
con.close() # TEARDOWN
@pytest.fixture
def repo(baza): # fixture kompozitsiyasi
return FoydalanuvchiRepo(baza)
@pytest.fixture
def toldirilgan_repo(repo):
repo.qosh("aziz")
repo.qosh("bek")
return repo
'''
TEST = '''
import pytest
def test_bosh_baza_bosh(repo):
assert repo.soni() == 0
def test_foydalanuvchi_qoshish(repo):
repo.qosh("aziz")
assert repo.soni() == 1
assert repo.ol("aziz") == {"login": "aziz", "faol": 1}
def test_har_test_toza_baza(repo):
# oldingi test 'aziz' qo'shdi, lekin bu test YANGI, bo'sh baza oladi
assert repo.soni() == 0
def test_takroriy_login_xato(repo):
repo.qosh("aziz")
with pytest.raises(Exception): # UNIQUE buzilishi
repo.qosh("aziz")
def test_faolsizlantirish(toldirilgan_repo):
assert toldirilgan_repo.faolsizlantir("aziz") is True
assert toldirilgan_repo.ol("aziz")["faol"] == 0
def test_yoq_foydalanuvchi(toldirilgan_repo):
assert toldirilgan_repo.faolsizlantir("nodira") is False
def test_toldirilgan_baza_soni(toldirilgan_repo):
assert toldirilgan_repo.soni() == 2
'''
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 / "repo.py").write_text(KOD, encoding="utf-8")
(r / "conftest.py").write_text(CONFTEST, encoding="utf-8")
(r / "test_repo.py").write_text(TEST, encoding="utf-8")
print("=== 1. conftest.py fixture lari 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"\n chiqish kodi: {kod}")
print(" ⭐ conftest fixture lari import qilinmasdan barcha testga ko'rindi")
print(" ⭐ har test yield fixture orqali toza, izolyatsiyalangan baza oldi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. conftest.py fixture lari bilan testlar ===
test_repo.py::test_bosh_baza_bosh PASSED
test_repo.py::test_foydalanuvchi_qoshish PASSED
test_repo.py::test_har_test_toza_baza PASSED
test_repo.py::test_takroriy_login_xato PASSED
test_repo.py::test_faolsizlantirish PASSED
test_repo.py::test_yoq_foydalanuvchi PASSED
test_repo.py::test_toldirilgan_baza_soni PASSED
--- 7 passed ---
chiqish kodi: 0
⭐ conftest fixture lari import qilinmasdan barcha testga ko'rindi
⭐ har test yield fixture orqali toza, izolyatsiyalangan baza oldiNima ko'rsatdi: 2.3, 2.5, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Fixture ni qo'lda chaqirish kerak" | pytest parametr nomidan o'zi chaqiradi |
| "Bir fixture barcha testda umumiy nusxa" | Sukut function scope — har test yangi |
| "Tozalash uchun alohida kod kerak" | yield fixture teardownni beradi |
"yield dan keyingi kod test yiqilsa bajarilmaydi" |
Bajariladi — try/finally kabi |
| "Fixture faqat ma'lumot qaytaradi" | Ulanish, fayl, holat — istalgan resurs |
"conftest.py ni import qilish kerak" |
pytest uni avtomatik topadi |
"scope=session doim tezroq va yaxshiroq" |
Umumiy holat testlarni bog'lashi mumkin |
"monkeypatch o'zgarishini qo'lda tiklash kerak" |
Test tugagach avtomatik tiklanadi |
6. Keng tarqalgan xatolar va yechimlari
1. Tayyorlashni har testda takrorlash
def test_a():
s = Savat(); s.qosh("kitob") # ❌ takror
...
@pytest.fixture
def savat(): # ✅
s = Savat(); s.qosh("kitob"); return s2. Fixture ni funksiya sifatida chaqirish
def test_x():
data = malumot() # ❌ fixture funksiya emas
def test_x(malumot): # ✅ parametr sifatida
...3. Tozalashni yield siz
@pytest.fixture
def baza():
con = och()
return con # ❌ con.close() hech qachon
@pytest.fixture
def baza():
con = och(); yield con; con.close() # ✅4. O'zgartiriladigan ma'lumotni keng scope bilan
@pytest.fixture(scope="session") # ❌ testlar bir-birini buzadi
def royxat(): return []
@pytest.fixture # ✅ har test yangi
def royxat(): return []5. tmp_path o'rniga qattiq kodlangan yo'l
def test_fayl():
Path("/tmp/test.txt").write_text(...) # ❌ izolyatsiya yo'q, tozalanmaydi
def test_fayl(tmp_path): # ✅
(tmp_path / "test.txt").write_text(...)6. os.environ ni to'g'ridan-to'g'ri o'zgartirish
def test_x():
os.environ["REJIM"] = "test" # ❌ keyingi testga ta'sir qiladi
def test_x(monkeypatch): # ✅ avtomatik tiklanadi
monkeypatch.setenv("REJIM", "test")7. Fixture ni conftest.py o'rniga import qilish
from test_savat import savat # ❌ testni import qilish
# ✅ conftest.py ga qo'ying — pytest avtomatik topadi7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 13.5-dars (o'tilgan): dependency injection — fixture aynan shu
- 16.7-dars (o'tilgan): sqlite3 — baza fixture
- 17.4-dars:
parametrize— fixture bilan birga ishlatiladi - 17.5–17.6-darslar: mock —
monkeypatchning kuchliroq versiyasi - 20.7-dars: FastAPI
Depends— fixture ga o'xshash mexanizm - 20.15-dars: FastAPI ilovasini test qilish — fixture bilan
8. Eng yaxshi amaliyotlar
Takroriy tayyorlashni fixture ga chiqaring.
Resurslar (fayl, ulanish) uchun
yieldfixture — teardown bilan.Mustaqillik uchun sukut
functionscope da qoldiring.Umumiy fixture larni
conftest.pyga joylang.Vaqtinchalik fayllar uchun
tmp_path, env/atribut uchunmonkeypatch.Murakkab tayyorlashni fixture kompozitsiyasi bilan qismlarga bo'ling.
autouseni ehtiyotkorlik bilan — yashirin xatti-harakat.Fixture nomlari tavsiflovchi bo'lsin:
bosh_savat,toldirilgan_repo.
9. Amaliy topshiriq
Vazifa 1: Natijani bashorat qiling
Har test uchun PASSED yoki FAILED ni ayting (fixture xatti-harakatiga qarab):
import pytest
@pytest.fixture
def royxat():
return [1, 2, 3]
HODISALAR = []
@pytest.fixture
def kuzatuvchi():
HODISALAR.append("setup")
yield "x"
HODISALAR.append("teardown")
1. def test_a(royxat): assert sum(royxat) == 6
2. def test_b(royxat):
royxat.append(4)
assert len(royxat) == 4
3. def test_c(royxat): assert len(royxat) == 3
4. def test_d(kuzatuvchi): assert kuzatuvchi == "x"
5. def test_e(): assert HODISALAR == ["setup", "teardown"]
6. def test_f(tmp_path):
(tmp_path / "a.txt").write_text("x")
assert (tmp_path / "a.txt").exists()
7. def test_g(tmp_path): assert not (tmp_path / "a.txt").exists()
8. def test_h(monkeypatch):
monkeypatch.setenv("X", "1")
import os; assert os.environ["X"] == "1"
9. def test_i():
import os; assert "X" not in os.environ
10. def test_j(capsys):
print("salom")
assert capsys.readouterr().out == "salom\n"Javoblar
PASSEDPASSEDPASSED— har test yangi ro'yxat oladi (2-test ta'sir qilmaydi)PASSEDPASSED— 4-test tugagach teardown bajarildiPASSEDPASSED— har test yangitmp_pathPASSEDPASSED—monkeypatchenv ni tikladiPASSED
Vazifa 2: Xatolarni tuzating
1. def test_x():
data = malumot_fixture() # fixture ni chaqirish
assert data["nom"] == "test"
2. @pytest.fixture
def baza():
con = sqlite3.connect(":memory:")
return con # tozalash yo'q
3. @pytest.fixture(scope="session")
def savat():
return [] # o'zgartiriladigan, umumiy
4. def test_fayl():
with open("/tmp/hisobot.txt", "w") as f:
f.write("x")
assert os.path.exists("/tmp/hisobot.txt")
5. def test_env():
os.environ["REJIM"] = "test"
assert sozlama() == "test"Javoblar
1. def test_x(malumot_fixture): # parametr sifatida
assert malumot_fixture["nom"] == "test"
2. @pytest.fixture
def baza():
con = sqlite3.connect(":memory:")
yield con # yield + teardown
con.close()
3. @pytest.fixture # sukut function scope
def savat():
return []
4. def test_fayl(tmp_path): # tmp_path
fayl = tmp_path / "hisobot.txt"
fayl.write_text("x")
assert fayl.exists()
5. def test_env(monkeypatch): # monkeypatch
monkeypatch.setenv("REJIM", "test")
assert sozlama() == "test"Vazifa 3: Fixture to'plami
Blog ilovasi uchun (post, izoh, foydalanuvchi):
conftest.pydafoydalanuvchi,post,izohli_postfixture lari (kompozitsiya)- Baza
yieldfixture bilan (SQLite:memory:) - Har test toza baza olishini isbotlovchi test
pytest -vda barcha o'tsin
Vazifa 4: monkeypatch bilan izolyatsiya
Tashqi API ga murojaat qiladigan funksiyani test qiling:
monkeypatch.setattrbilan tarmoq chaqiruvini soxta funksiyaga almashtiring- Turli javoblar (muvaffaqiyat, xato, timeout) uchun testlar
monkeypatch.setenvbilan API kalitini sozlang- Haqiqiy tarmoqqa chiqmasligini ta'minlang
Vazifa 5: Scope tajribasi
function,module,sessionscope li fixture lar yarating- Har biri necha marta chaqirilishini sanang (global hisoblagich)
pytest --setup-showbilan chaqiruv tartibini kuzating- Qaysi holatda qaysi scope to'g'ri kelishini yozing
Vazifa 6: autouse va tozalash
autouse=Truefixture bilan har testdan oldin global holatni tiklang- Uning yashirin ekanini va testni tushunishga ta'sirini muhokama qiling
- Aniq (so'raladigan) fixture bilan solishtiring
- Qaysi biri afzalroq — xulosa
Vazifa 7: O'ylash
Fixture — pytest ning eng o'ziga xos va boshqa til/vositalarda kam uchraydigan xususiyati. unittest da setUp/tearDown metodlari bor edi — nima uchun pytest ularni fixture bilan almashtirdi, va fixture ning "so'rov bo'yicha in'eksiya" modeli setUp dan nimasi bilan yaxshiroq?
Javob
Qisqa javob: setUp/tearDown har testdan oldin/keyin bir xil ishni bajaradi — test nima kerakligini tanlay olmaydi, hammasi tayyorlanadi. Fixture esa so'rov bo'yicha: test faqat kerakli fixture larni parametr sifatida so'raydi, pytest ularni (va ular bog'liq bo'lgan boshqa fixture larni) tayyorlaydi. Bu modullilik, qayta ishlatish va aniqlik beradi — har testning nimaga bog'liqligi uning imzosida ko'rinadi.
1. setUp/tearDown cheklovlari
| Muammo | Tafsilot |
|---|---|
| Hammasi yoki hech narsa | Har test butun setUp ni oladi, kerak-kermas |
| Modullik yo'q | Bitta katta setUp, qismlarga bo'linmaydi |
| Qayta ishlatish qiyin | Boshqa sinfda takrorlash kerak |
| Bog'liqlik ko'rinmaydi | Test nimaga tayangani noaniq |
| Scope cheklangan | Faqat sinf darajasida |
2. Fixture afzalliklari
| Xususiyat | Foyda |
|---|---|
| So'rov bo'yicha | Test faqat kerakligini oladi |
| Bog'liqlik imzoda | def test_x(baza, repo) — aniq |
| Kompozitsiya | Fixture fixture ni ishlatadi |
| Qayta ishlatish | conftest.py — hamma joyda |
| Moslashuvchan scope | function/class/module/session |
| Avtomatik teardown | yield bilan |
3. "So'rov bo'yicha in'eksiya" nima beradi
Bu — dependency injection naqshi 13.5-bob. Test o'z bog'liqliklarini e'lon qiladi, tizim ularni yetkazadi. Natijada:
- Test o'qilishi oson (nimaga bog'liq — ko'rinadi)
- Testlar tez (faqat kerakli tayyorlanadi)
- Refaktoring oson (fixture bir joyda o'zgaradi)
4. Narxi
| Kamchilik | Izoh |
|---|---|
| O'rganish egri chizig'i | "Sehr" kabi ko'rinadi boshda |
Yashirin bog'liqlik (autouse) |
Ehtiyot bo'lish kerak |
| Nomlash muhim | Fixture nomi ma'noli bo'lsin |
5. Muhandislik saboqlari
- So'rov bo'yicha (explicit) model hammasi-yoki-hech-narsadan yaxshi
- Bog'liqlikni ko'rinadigan qilish — test va kodda foydali
- Kompozitsiya — kichik qismlardan murakkab tuzilma
- DI naqshi test'dan tashqarida ham (FastAPI, 20.7) qo'llaniladi
6. Xulosa
setUphammani tayyorlaydi; fixture — so'rov bo'yicha- Bog'liqlik test imzosida ko'rinadi
- Kompozitsiya va qayta ishlatish — fixture kuchi
- Bu dependency injection naqshining test'dagi ko'rinishi
Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.
Xulosa
Bu darsda fixture larni — testga kerak narsani tayyorlab, tozalab beruvchi mexanizmni o'rgandik.
Eng muhim uch fikr:
Fixture takrorni yo'q qiladi va mustaqillik beradi.
@pytest.fixturebilan belgilangan funksiya testga kerakli narsani (obyekt, ma'lumot, ulanish) tayyorlaydi; test uni parametr nomi orqali so'raydi vapytestavtomatik chaqiradi — bu dependency injection. Sukutfunctionscope da har test yangi, toza nusxa oladi, shuning uchun testlar bir-biriga ta'sir qilmaydi.yieldfixture — setup va teardown.yieldgacha tayyorlash, keyin tozalash bajariladi — va tozalash test yiqilsa ham ishlaydi (try/finallykabi). Bu fayl, ulanish, server kabi resurslarni to'g'ri boshqarishning standart usuli. Scope (function/class/module/session) fixture qancha marta chaqirilishini belgilaydi — kengroq tezroq, lekin o'zgartiriladigan holatda ehtiyot bo'ling.O'rnatilgan fixture lar va
conftest.py.tmp_path(izolyatsiyalangan vaqtinchalik fayl),monkeypatch(env/atributni vaqtincha almashtirish, avtomatik tiklash),capsys(chiqishni ushlash) — tayyor, kuchli vositalar. Bir necha faylda ishlatiladigan fixture larniconftest.pyga joylang —pytestuni import qilmasdan avtomatik topadi. Murakkab tayyorlashni fixture kompozitsiyasi bilan kichik qismlarga bo'ling.
Keyingi darsda parametrize ni o'rganamiz: bitta testni ko'p xil kirish-natija juftligi bilan ishga tushirib, o'nlab holatni qisqa, takrorsiz kod bilan qamrab olish.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!