14-bo‘lim
Qoplama va uning chegaralari
Qoplamani o'lchash, qator va shox qoplamasi farqi, 100% qoplamadagi xato, mutatsion sinov va qoplama raqamini maqsadga aylantirish xavfi.
Ushbu bo‘lim mundarijasi
Qoplama (coverage) - testlar ishga tushirganda kodning qaysi qismlari bajarilganini ko'rsatadigan o'lchov.
U juda foydali vosita. Va u juda ko'p noto'g'ri tushuniladi.
O'lchashni boshlash #
pip install pytest-cov
Collecting pytest-cov
Downloading pytest_cov-7.1.0-py3-none-any.whl.metadata (32 kB)
Collecting coverage>=7.10.6 (from coverage[toml]>=7.10.6->pytest-cov)
Downloading coverage-7.16.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl.metadata (8.2 kB)
Requirement already satisfied: pluggy>=1.2 in ./.venv/lib/python3.13/site-packages (from pytest-cov) (1.6.0)
Requirement already satisfied: pytest>=7 in ./.venv/lib/python3.13/site-packages (from pytest-cov) (9.1.1)
Requirement already satisfied: iniconfig>=1.0.1 in ./.venv/lib/python3.13/site-packages (from pytest>=7->pytest-cov) (2.3.0)
Requirement already satisfied: packaging>=22 in ./.venv/lib/python3.13/site-packages (from pytest>=7->pytest-cov) (26.3)
Requirement already satisfied: pygments>=2.7.2 in ./.venv/lib/python3.13/site-packages (from pytest>=7->pytest-cov) (2.21.0)
Downloading pytest_cov-7.1.0-py3-none-any.whl (22 kB)
Downloading coverage-7.16.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (257 kB)
Installing collected packages: coverage, pytest-cov
Successfully installed coverage-7.16.1 pytest-cov-7.1.0
def yetkazish_narxi(summa, shahar):
"""500 000 so'mdan katta xaridda yetkazish bepul."""
narx = 20_000
if summa > 500_000:
narx = 0
if shahar != "Toshkent":
narx += 15_000
return narx
from yetkazish import yetkazish_narxi
def test_toshkentda_katta_xarid_bepul():
assert yetkazish_narxi(600_000, "Toshkent") == 0
def test_boshqa_shaharda_kichik_xarid():
assert yetkazish_narxi(100_000, "Samarqand") == 35_000
pytest -q --cov=yetkazish --cov-report=term-missing test_yetkazish.py
.. [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Cover Missing
--------------------------------------------
yetkazish.py 7 0 100%
--------------------------------------------
TOTAL 7 0 100%
2 passed in 0.02s
100%. Hamma qator bajarildi, ikkala shart ham rost va yolg'on tomonidan sinaldi.
Endi diqqat bilan qarang - kodda xato bor.
100% qoplama va yashirin xato #
Qoida shunday edi: 500 000 so'mdan katta xaridda yetkazish bepul. Samarqandga 600 000 so'mlik buyurtma bersak nima bo'ladi?
from yetkazish import yetkazish_narxi
def test_toshkentda_katta_xarid_bepul():
assert yetkazish_narxi(600_000, "Toshkent") == 0
def test_boshqa_shaharda_kichik_xarid():
assert yetkazish_narxi(100_000, "Samarqand") == 35_000
def test_boshqa_shaharda_katta_xarid_ham_bepul():
assert yetkazish_narxi(600_000, "Samarqand") == 0
pytest -q --tb=line --cov=yetkazish test_yetkazish.py
..F [100%]
=============================== FAILURES ===============================
E AssertionError: assert 15000 == 0
+ where 15000 = yetkazish_narxi(600000, 'Samarqand')
/home/dasturchi/loyiha/test_yetkazish.py:13: AssertionError: assert 15000 == 0
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Cover
----------------------------------
yetkazish.py 7 0 100%
----------------------------------
TOTAL 7 0 100%
======================= short test summary info ========================
FAILED test_yetkazish.py::test_boshqa_shaharda_katta_xarid_ham_bepul
1 failed, 2 passed in 0.03s
Mana xato: mijoz 600 000 so'mlik buyurtma bergan, lekin undan 15 000 so'm olinmoqda. Va bu qoplama 100% bo'lgan holda sodir bo'ldi.
Bizning kodimizda ikkita shart bor - demak to'rtta kombinatsiya:
| Summa | Shahar | Sinaldimi |
|---|---|---|
| Katta | Toshkent | Ha |
| Kichik | Boshqa | Ha |
| Kichik | Toshkent | Yo'q |
| Katta | Boshqa | Yo'q - xato shu yerda |
Ikkita test to'rtta holatdan ikkitasini qamradi va barcha qatorlarni bajardi. Qoplama 100% ko'rsatdi.
Qoida: qoplama testlar nimani bajarganini ko'rsatadi, nimani tekshirganini emas.
Kodni tuzatamiz:
def yetkazish_narxi(summa, shahar):
"""500 000 so'mdan katta xaridda yetkazish bepul."""
if summa > 500_000:
return 0
narx = 20_000
if shahar != "Toshkent":
narx += 15_000
return narx
pytest -q --cov=yetkazish --cov-report=term-missing test_yetkazish.py
... [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Cover Missing
--------------------------------------------
yetkazish.py 7 0 100%
--------------------------------------------
TOTAL 7 0 100%
3 passed in 0.02s
Qator va shox qoplamasi #
Ikki xil o'lchov bor va ular sezilarli farq qiladi:
Buni amalda ko'ramiz:
def chegirma(summa, talaba):
if talaba:
summa = summa * 0.5
return round(summa)
from chegirma import chegirma
def test_talabaga_yarim_narx():
assert chegirma(100_000, True) == 50_000
pytest -q --cov=chegirma --cov-report=term-missing test_chegirma.py
. [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Cover Missing
-------------------------------------------
chegirma.py 4 0 100%
-------------------------------------------
TOTAL 4 0 100%
1 passed in 0.02s
Endi shox qoplamasi bilan:
pytest -q --cov=chegirma --cov-branch --cov-report=term-missing test_chegirma.py
. [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Branch BrPart Cover Missing
---------------------------------------------------------
chegirma.py 4 0 2 1 83% 2->4
---------------------------------------------------------
TOTAL 4 0 2 1 83%
1 passed in 0.02s
Bitta bayroq yangi ma'lumot berdi. Missing ustunidagi 2->4 yozuvi
shuni bildiradi: 2-qatordan 4-qatorga o'tish hech qachon sodir
bo'lmagan - ya'ni talaba yolg'on bo'lgan yo'l sinalmagan.
Qator qoplamasi hamon 100% ko'rsatardi, shox qoplamasi esa teshikni topdi.
Mutatsion sinov: qoplamani tekshirish #
Qoplama testlarning borligini o'lchaydi, kuchini emas. Testlaringiz haqiqatan ish beradimi - buni bilishning ishonchli yo'li kodni ataylab buzish va testlar buni sezishini kuzatish.
Bu usul mutatsion sinov (mutation testing) deb ataladi. Uni qo'lda ham qilish mumkin:
def chegirma(summa, talaba):
if not talaba: # mutatsiya: shart teskari qilindi
summa = summa * 0.5
return round(summa)
pytest -q --tb=no --cov=chegirma test_chegirma.py
F [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Cover
---------------------------------
chegirma.py 4 1 75%
---------------------------------
TOTAL 4 1 75%
======================= short test summary info ========================
FAILED test_chegirma.py::test_talabaga_yarim_narx - assert 100000 == ...
1 failed in 0.02s
Test yiqildi - demak u haqiqatan ishlaydi. Endi boshqa mutatsiya:
def chegirma(summa, talaba):
if talaba:
summa = summa * 0.5
return round(summa + 0.4) # mutatsiya: kichik qo'shimcha
pytest -q --tb=no --cov=chegirma test_chegirma.py
. [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Cover
---------------------------------
chegirma.py 4 0 100%
---------------------------------
TOTAL 4 0 100%
1 passed in 0.02s
Bu safar test sezmadi. round(50000.4) ham 50000 ga teng - ya'ni
testimiz yumaloqlash mantig'ini umuman tekshirmaydi.
Mana shu - qoplama ko'rsatolmaydigan ma'lumot. Har bir omon qolgan mutatsiya - testlaringizdagi teshik.
Python da mutmut va cosmic-ray kutubxonalari shu ishni o'zi
qiladi: kodingizga yuzlab kichik o'zgarish kiritadi va har biri uchun
testlarni ishga tushiradi.
Natija "mutatsion ball" ko'rinishida beriladi: nechta mutatsiya o'ldirilgan (test tutgan) va nechtasi omon qolgan.
Bu qoplamadan ancha ishonchli o'lchov. Kamchiligi - sekinligi: testlar yuzlab marta ishga tushiriladi. Shuning uchun uni odatda kodning eng muhim qismlari uchun, haftada bir marta ishlatishadi.
Kodni qaytaramiz:
def chegirma(summa, talaba):
if talaba:
summa = summa * 0.5
return round(summa)
Qoplama raqami maqsadga aylanganda #
Eng yomon holat - assert siz yozilgan "test":
from yetkazish import yetkazish_narxi
def test_yetkazish_ishlaydi():
yetkazish_narxi(600_000, "Toshkent")
yetkazish_narxi(100_000, "Samarqand")
pytest -q --cov=yetkazish --cov-report=term-missing test_qopqoq.py
. [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Cover Missing
--------------------------------------------
yetkazish.py 7 0 100%
--------------------------------------------
TOTAL 7 0 100%
1 passed in 0.02s
Qoplama 100%, tekshirilgan narsa - nol. Funksiya nima qaytarsa ham bu test o'tadi.
Uch qoida yetarli:
- Qoplamani yiqilgan raqam sifatida emas, ro'yxat sifatida o'qing.
--cov-report=term-missingqaysi qatorlar sinalmaganini ko'rsatadi. O'sha ro'yxatni ko'rib chiqing: bu qator haqiqatan kerakmi, kerak bo'lsa nima uchun testi yo'q? - Qoplamaning tushishini kuzating, darajasini emas. Yangi kod qoplamasiz kelayotgan bo'lsa - bu signal. 87% dan 86% ga tushish esa hech narsani anglatmaydi.
- Nol qoplamali fayllarni qidiring. Odatda eng katta xavf shu yerda: butun modul hech qachon sinalmagan.
Nimani hisobdan chiqarish kerak #
Ba'zi kod qoplamaga kirmasligi kerak - u sizni chalg'itadi:
[run]
branch = True
omit =
*/.venv/*
test_*.py
*/migrations/*
[report]
exclude_lines =
pragma: no cover
if __name__ == .__main__.:
raise NotImplementedError
pytest -q --cov=. --cov-report=term test_yetkazish.py
... [100%]
============================ tests coverage ============================
___________ coverage: platform linux, python 3.13.12-final-0 ___________
Name Stmts Miss Branch BrPart Cover
------------------------------------------------
chegirma.py 4 4 2 0 0%
yetkazish.py 7 0 4 1 91%
------------------------------------------------
TOTAL 11 4 6 1 59%
3 passed in 0.03s
Sozlama fayli tufayli endi --cov-branch ni har safar yozish shart
emas, virtual muhit va test fayllarining o'zi esa hisobga olinmaydi.
Va hisobot darhol bitta narsani ko'rsatdi: chegirma.py - 0%. Biz
uni bu ishga tushirishda umuman sinamadik. Aynan shunday qatorlar -
qoplama hisobotining eng qimmatli qismi.
pytest-covni o'rnatib,yetkazish.pyuchun qoplamani o'lchang.- Qoplama 100% ekaniga ishonch hosil qiling va keyin xatoni toping.
- To'rtta kombinatsiyani jadval qilib chizing va qaysilari sinalganini belgilang.
- Uchinchi testni qo'shib, xatoni tuting va kodni tuzating.
chegirma.pyuchun qator va shox qoplamasini solishtiring.- Farq nechaga teng chiqdi?
- Shartni teskari qilib, mutatsiya testini o'zingiz o'tkazing.
- Testlar sezmaydigan yana bitta mutatsiya toping.
assertsiz test yozib, uning qoplamasini o'lchang.- O'z loyihangizda nol qoplamali fayl bormi - qidirib ko'ring.
Xulosa #
- Qoplama - testlar ishga tushganda kodning qaysi qismlari bajarilganini ko'rsatadi.
pytest --cov=modul --cov-report=term-missingqoplamani va sinalmagan qatorlarni ko'rsatadi.- 100% qoplama xatolar yo'q degani emas: ikkita shart to'rtta kombinatsiya beradi.
- Qoplama testlar nimani bajarganini ko'rsatadi, nimani tekshirganini emas.
- Qator qoplamasi qatorlarni, shox qoplamasi esa har
ifning ikkala yo'nalishini sanaydi. --cov-branchbepul va ancha ko'proq ma'lumot beradi - uni har doim yoqing.- Mutatsion sinov kodni ataylab buzib, testlar buni sezishini tekshiradi.
- Omon qolgan har bir mutatsiya - testlardagi teshik;
mutmutvacosmic-raybuni avtomatlashtiradi. assertsiz yozilgan "test" 100% qoplama beradi va hech narsani tekshirmaydi.- Qoplama raqami maqsadga aylansa, u o'lchov bo'lishdan to'xtaydi.
- Qoplamani ro'yxat sifatida o'qing:
term-missingdagi qatorlarni ko'rib chiqing. - Darajani emas, tushishni kuzating; nol qoplamali fayllarni alohida qidiring.
.coveragercfayli sozlamalarni bir joyda saqlaydi va keraksiz papkalarni hisobdan chiqaradi.
Keyingi bo'limda testlarning eng asabiy turiga o'tamiz: goh o'tib, goh yiqiladiganlariga.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.