Fixture, parametrize va qamrov
Takrorlanishdan qutulish
6-bobdagi testlarga qarang: test_son_emas, test_bosh_qiymat, test_chegaradan_yuqori,
test_manfiy — to'rttasi ham bir xil tuzilishda, faqat kirish va kutilgan xabar boshqa.
parametrize shuni bitta testga yig'adi:
shu faylni yaratingtests/test_tekshiruv.py
import pytest from hisobot.tekshiruv import bahoni_tekshir, sanani_tekshir @pytest.mark.parametrize("xom, kutilgan", [ ("85", 85), (" 90 ", 90), ("0", 0), ("100", 100), ("007", 7), ]) def test_togri_baholar(xom, kutilgan): assert bahoni_tekshir(xom) == kutilgan @pytest.mark.parametrize("xom, xabar", [ ("abc", "son emas"), ("", "son emas"), ("8.5", "son emas"), (None, "ko'rsatilmagan"), ("150", "oralig'ida"), ("-5", "oralig'ida"), ("101", "oralig'ida"), ]) def test_notogri_baholar(xom, xabar): with pytest.raises(ValueError, match=xabar): bahoni_tekshir(xom) @pytest.mark.parametrize("xom", ["2026-09-01", "2000-01-01", "2026-12-31"]) def test_togri_sanalar(xom): assert sanani_tekshir(xom) == xom @pytest.mark.parametrize("xom", ["01.09.2026", "2026-13-01", "2026-02-30", "", "kecha", None]) def test_notogri_sanalar(xom): with pytest.raises(ValueError): sanani_tekshir(xom)
.venv/bin/python3 -m pytest tests/test_tekshiruv.py -v
============================= test session starts ============================== platform linux -- Python 3.10.12, pytest-9.1.1, pluggy-1.6.0 rootdir: /mnt/c/Users/Admin/Desktop/Course Material/Python_Muhandisligi/ish configfile: pyproject.toml plugins: cov-7.1.0 collected 21 items tests/test_tekshiruv.py ..................... [100%] ============================== 21 passed in 0.29s ==============================
Endi 5 + 7 + 3 + 6 = 21 holat, lekin kod to'rtta funksiya. Har holat alohida test sifatida ishlaydi va nomida o'z qiymati ko'rinadi — bittasi yiqilsa, aynan qaysi kirish muammo qilgani darhol ma'lum.
Bu naqshning asosiy foydasi: yangi holat qo'shish bir qator yozish demak.
rm -f tests/test_sana.py .venv/bin/python3 -m pytest -q
..................... [100%]
Fixture: tayyorgarlikni ajratish
Testga ba'zan tayyor ma'lumot kerak: fayl, ulanish, obyekt. fixture — shuni beradigan funksiya.
shu faylni yaratingtests/conftest.py
"""Barcha testlar uchun umumiy fixture lar. pytest bu faylni o'zi topadi.""" import pytest from hisobot.modellar import Talaba @pytest.fixture def talabalar(): """Sinov uchun uchta talaba.""" return [ Talaba(ism="Aziz", guruh="A", baho=85, sana="2026-09-01"), Talaba(ism="Bobur", guruh="A", baho=95, sana="2026-09-01"), Talaba(ism="Dilnoza", guruh="B", baho=70, sana="2026-09-02"), ] @pytest.fixture def csv_fayl(tmp_path): """Vaqtinchalik CSV fayl yaratadi va yo'lini qaytaradi.""" yol = tmp_path / "sinov.csv" yol.write_text( "ism,guruh,baho,sana\n" "Aziz,A,85,2026-09-01\n" "Bobur,A,95,2026-09-01\n" "Xato,B,abc,2026-09-02\n", encoding="utf-8", ) return yol
shu faylni yaratingtests/test_hisob.py
from hisobot.hisob import daraja_taqsimoti, eng_yaxshi, guruh_ortachasi def test_guruh_ortachasi(talabalar): assert guruh_ortachasi(talabalar) == {"A": 90.0, "B": 70.0} def test_guruh_ortachasi_bosh(): assert guruh_ortachasi([]) == {} def test_daraja_taqsimoti(talabalar): # Aziz 85 -> yaxshi, Bobur 95 -> a'lo, Dilnoza 70 -> qoniqarli assert daraja_taqsimoti(talabalar) == {"yaxshi": 1, "a'lo": 1, "qoniqarli": 1} def test_eng_yaxshi(talabalar): eng = eng_yaxshi(talabalar) assert eng is not None assert eng.ism == "Bobur" def test_eng_yaxshi_bosh(): assert eng_yaxshi([]) is None
.venv/bin/python3 -m pytest tests/test_hisob.py -v
============================= test session starts ============================== platform linux -- Python 3.10.12, pytest-9.1.1, pluggy-1.6.0 rootdir: /mnt/c/Users/Admin/Desktop/Course Material/Python_Muhandisligi/ish configfile: pyproject.toml plugins: cov-7.1.0 collected 5 items tests/test_hisob.py ..... [100%] ============================== 5 passed in 0.06s ===============================
talabalar fixture nomi test funksiyasiga argument sifatida yozildi va pytest uni o'zi
uzatdi. conftest.py da turgani uchun uni import qilish shart emas.
tmp_path: vaqtinchalik fayllar
tmp_path — pytest ning tayyor fixture i. U har testga yangi bo'sh papka beradi va keyin o'zi
tozalaydi.
shu faylni yaratingtests/test_oqish.py
import pytest from hisobot.oqish import oqi, tozala def test_oqi_qatorlar_soni(csv_fayl): qatorlar = oqi(csv_fayl) assert len(qatorlar) == 3 assert qatorlar[0]["ism"] == "Aziz" def test_tozala_xatoni_ajratadi(csv_fayl): n = tozala(oqi(csv_fayl)) assert len(n.talabalar) == 2 assert len(n.xatolar) == 1 assert n.jami == 3 def test_xato_qator_raqami(csv_fayl): n = tozala(oqi(csv_fayl)) qator, ism, sabab = n.xatolar[0] assert qator == 4 # sarlavha 1-qator, ma'lumot 2 dan boshlanadi assert ism == "Xato" assert "son emas" in sabab def test_fayl_yoq(tmp_path): with pytest.raises(FileNotFoundError): oqi(tmp_path / "yoq.csv")
.venv/bin/python3 -m pytest tests/test_oqish.py -v
============================= test session starts ============================== platform linux -- Python 3.10.12, pytest-9.1.1, pluggy-1.6.0 rootdir: /mnt/c/Users/Admin/Desktop/Course Material/Python_Muhandisligi/ish configfile: pyproject.toml plugins: cov-7.1.0 collected 4 items tests/test_oqish.py .... [100%] ============================== 4 passed in 0.09s ===============================
Har test o'z papkasini oladi, shuning uchun ular bir-biriga xalaqit bermaydi va istalgan tartibda ishlaydi.
Buni ko'rsatamiz — papka yo'llarini chiqaramiz:
shu faylni yaratingtests/test_izolyatsiya.py
def test_birinchi(tmp_path): (tmp_path / "a.txt").write_text("birinchi") print("1-test papkasi:", tmp_path.name, "| fayllar:", [p.name for p in tmp_path.iterdir()]) def test_ikkinchi(tmp_path): print("2-test papkasi:", tmp_path.name, "| fayllar:", [p.name for p in tmp_path.iterdir()]) assert not list(tmp_path.iterdir())
.venv/bin/python3 -m pytest tests/test_izolyatsiya.py -v -s rm tests/test_izolyatsiya.py
============================= test session starts ============================== platform linux -- Python 3.10.12, pytest-9.1.1, pluggy-1.6.0 rootdir: /mnt/c/Users/Admin/Desktop/Course Material/Python_Muhandisligi/ish configfile: pyproject.toml plugins: cov-7.1.0 collected 2 items tests/test_izolyatsiya.py 1-test papkasi: test_birinchi0 | fayllar: ['a.txt'] .2-test papkasi: test_ikkinchi0 | fayllar: [] . ============================== 2 passed in 0.06s ===============================
Ikkinchi test bo'sh papka oldi — birinchisi yozgan fayl unga ko'rinmadi. Bu test izolyatsiyasi va u majburiy shart: testlar bir-biriga bog'liq bo'lsa, ularning natijasiga ishonib bo'lmaydi.
Fixture qamrovi (scope)
Qimmat tayyorgarlikni har testda takrorlash shart emas:
shu faylni yaratingtests/test_scope.py
import pytest CHAQIRUVLAR = {"har_test": 0, "modul": 0} @pytest.fixture def har_test(): CHAQIRUVLAR["har_test"] += 1 return CHAQIRUVLAR["har_test"] @pytest.fixture(scope="module") def modul_uchun(): CHAQIRUVLAR["modul"] += 1 return CHAQIRUVLAR["modul"] def test_a(har_test, modul_uchun): pass def test_b(har_test, modul_uchun): pass def test_c(har_test, modul_uchun): print(f"\nhar test uchun chaqirildi: {CHAQIRUVLAR['har_test']} marta") print(f"modul uchun chaqirildi : {CHAQIRUVLAR['modul']} marta")
.venv/bin/python3 -m pytest tests/test_scope.py -s -q rm tests/test_scope.py
.. har test uchun chaqirildi: 3 marta modul uchun chaqirildi : 1 marta .
Uch testda har_test uch marta, modul_uchun bir marta chaqirildi.
scope |
Qachon qayta yaratiladi |
|---|---|
function (odatiy) |
Har test uchun |
class |
Har sinf uchun |
module |
Har fayl uchun |
session |
Butun ishga tushirish uchun bir marta |
Ehtiyot bo'ling: keng qamrovli fixture o'zgartirilsa, testlar bir-biriga ta'sir qiladi. Uni faqat o'zgarmas narsalar uchun ishlating (ulanish, o'qish uchun ma'lumot).
Qamrov (coverage): kod qancha sinaldi
Qamrovni o'lchash uchun pytest-cov kerak. U 6-bobdagi dev guruhga keyinroq qo'shilgan, shuning
uchun uni hozir o'rnatamiz:
.venv/bin/python3 -m pip install --quiet pytest-cov .venv/bin/python3 -m pytest --cov=hisobot --cov-report=term-missing
.............................. [100%] ================================ tests coverage ================================ _______________ coverage: platform linux, python 3.10.12-final-0 _______________ Name Stmts Miss Cover Missing -------------------------------------------------------- src/hisobot/__init__.py 6 0 100% src/hisobot/__main__.py 18 18 0% 2-24 src/hisobot/hisob.py 12 0 100% src/hisobot/modellar.py 33 1 97% 20 src/hisobot/oqish.py 15 0 100% src/hisobot/tekshiruv.py 20 0 100% -------------------------------------------------------- TOTAL 104 19 82% 30 passed in 0.39s
Missing ustuni — hech qachon bajarilmagan qatorlar. Ular sinovsiz kod.
Qamrovni foizga aylantirib, chegara qo'yish mumkin:
shu faylni yaratingpyproject.toml
[project]
name = "hisobot"
version = "0.7.0"
description = "CSV ma'lumotini tekshirib hisobot chiqaradigan asbob"
requires-python = ">=3.10"
dependencies = []
[project.optional-dependencies]
dev = [
"pytest>=8",
"pytest-cov>=5",
"mypy>=1.8",
"ruff>=0.5",
]
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q --strict-markers"
[tool.coverage.run]
source = ["hisobot"]
[tool.coverage.report]
show_missing = true
exclude_lines = [
"pragma: no cover",
"if __name__ == .__main__.:",
"raise NotImplementedError",
]
.venv/bin/python3 -m pytest --cov=hisobot --cov-report=term-missing --cov-fail-under=80
.............................. [100%] ================================ tests coverage ================================ _______________ coverage: platform linux, python 3.10.12-final-0 _______________ Name Stmts Miss Cover Missing -------------------------------------------------------- src/hisobot/__init__.py 6 0 100% src/hisobot/__main__.py 16 16 0% 2-20 src/hisobot/hisob.py 12 0 100% src/hisobot/modellar.py 33 1 97% 20 src/hisobot/oqish.py 15 0 100% src/hisobot/tekshiruv.py 20 0 100% -------------------------------------------------------- TOTAL 102 17 83% Required test coverage of 80% reached. Total coverage: 83.33% 30 passed in 0.37s
--cov-fail-under=80 — qamrov 80% dan past bo'lsa pytest xato qaytaradi. CI da shu ishlatiladi.
Qamrov yolg'on gapirishi mumkin
100% qamrov "xato yo'q" degani emas. Buni ko'rsatamiz:
shu faylni yaratingyomon_test.py
def bol(a, b): return a / b
shu faylni yaratingnamuna/test_yomon.py
from yomon_test import bol def test_bol(): bol(10, 2) # natija tekshirilmagan!
.venv/bin/python3 -m pytest namuna/test_yomon.py --cov=yomon_test --cov-report=term-missing -q
. [100%] ================================ tests coverage ================================ _______________ coverage: platform linux, python 3.10.12-final-0 _______________ Name Stmts Miss Cover Missing --------------------------------------------- yomon_test.py 2 0 100% --------------------------------------------- TOTAL 2 0 100%
Qamrov 100% — lekin test hech narsani tekshirmaydi. bol(10, 0) nima qilishi ham noma'lum.
.venv/bin/python3 -m pytest -q
.............................. [100%]
Saboq: qamrov — qaysi kod sinalmaganini ko'rsatadigan asbob, sifat o'lchovi emas. 60% dan past bo'lsa xavotir oling; 100% ni maqsad qilmang.
Butun to'plam
.venv/bin/python3 -m pytest --cov=hisobot --cov-report=term-missing python3 -m mypy
.............................. [100%] ================================ tests coverage ================================ _______________ coverage: platform linux, python 3.10.12-final-0 _______________ Name Stmts Miss Cover Missing -------------------------------------------------------- src/hisobot/__init__.py 6 0 100% src/hisobot/__main__.py 16 16 0% 2-20 src/hisobot/hisob.py 12 0 100% src/hisobot/modellar.py 33 1 97% 20 src/hisobot/oqish.py 15 0 100% src/hisobot/tekshiruv.py 20 0 100% -------------------------------------------------------- TOTAL 102 17 83% 30 passed in 1.12s Success: no issues found in 6 source files
Testlar o'tadi, turlar toza. Loyiha shu ikki buyruq bilan tekshiriladi — 16-bobda ularni CI ga qo'yamiz.
Xulosa
| Tushuncha | Asosiy fikr |
|---|---|
parametrize |
Bir test, ko'p holat; har biri alohida ishlaydi |
fixture |
Tayyorgarlikni ajratadi; argument nomi bilan chaqiriladi |
conftest.py |
Umumiy fixture lar; import qilish shart emas |
tmp_path |
Har testga yangi bo'sh papka — izolyatsiya kafolati |
scope |
Qimmat tayyorgarlikni takrorlamaslik; lekin o'zgaruvchan bo'lmasin |
| Qamrov | Qaysi qator sinalmaganini ko'rsatadi |
| Qamrov cheklovi | 100% qamrov ham bo'sh test bilan olinadi — u sifat o'lchovi emas |
Talaba ishi
--cov-report=term-missingchiqishidagiMissingqatorlariga qarang. Ularning biriga test yozing. Qamrov necha foizga oshdi?csv_faylfixture iniparametrizebilan birlashtiring: uchta turli xil CSV (bo'sh, faqat sarlavha, faqat xato qatorlar). Har biri uchuntozalanima qaytaradi?conftest.pygascope="session"fixture qo'shing va uni ikki testda o'zgartiring. Qanday muammo yuzaga keladi?pytest --durations=5ni ishga tushiring. Eng sekin testlar qaysilari va nega?
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.