3-bo‘lim
Assert, istisno va xato xabarini o'qish
Turli qiymatlarni solishtirish, kasr sonlar bilan tuzoq, pytest.approx, istisnolarni tekshirish va foydali xato xabarlari yozish.
Ushbu bo‘lim mundarijasi
Test - bu assert. Boshqa hamma narsa (fixture, dublyor, parametr) - shu
bitta amal atrofidagi qulayliklar.
Shuning uchun bu bo'limni to'liq assert ga bag'ishlaymiz.
Oddiy solishtirishlar #
pytest da maxsus tasdiqlash metodlari yo'q - Python ning o'z operatorlari ishlatiladi:
def test_sonlar():
assert 2 + 2 == 4
assert 10 > 3
assert 7 != 8
def test_satrlar():
assert "salom".upper() == "SALOM"
assert "dastur" in "dasturlash"
assert "abc".startswith("a")
def test_royxatlar():
assert [1, 2, 3] == [1, 2, 3]
assert len([1, 2, 3]) == 3
assert 2 in [1, 2, 3]
def test_lugatlar():
talaba = {"ism": "Aziza", "baho": 90}
assert talaba["baho"] == 90
assert "ism" in talaba
pytest -q test_solishtirish.py
.... [100%]
4 passed in 0.01s
Har bir tur uchun alohida metod yo'q - ==, in, > yetarli.
Xato xabari turga qarab o'zgaradi #
pytest solishtirilayotgan narsaning turini biladi va farqni o'sha turga mos ko'rinishda ko'rsatadi. Ro'yxatlarda:
def test_royxat_farqi():
kutilgan = ["olma", "anor", "uzum", "shaftoli"]
olingan = ["olma", "anor", "nok", "shaftoli"]
assert olingan == kutilgan
pytest -q test_farq.py
F [100%]
=============================== FAILURES ===============================
__________________________ test_royxat_farqi ___________________________
def test_royxat_farqi():
kutilgan = ["olma", "anor", "uzum", "shaftoli"]
olingan = ["olma", "anor", "nok", "shaftoli"]
> assert olingan == kutilgan
E AssertionError: assert ['olma', 'ano...', 'shaftoli'] == ['olma', 'ano...', 'shaftoli']
E
E At index 2 diff: 'nok' != 'uzum'
E Use -v to get more diff
test_farq.py:4: AssertionError
======================= short test summary info ========================
FAILED test_farq.py::test_royxat_farqi - AssertionError: assert ['olm...
1 failed in 0.01s
pytest qaysi indeksda farq borligini o'zi topdi. Lug'atlar bilan ham shunday:
def test_lugat_farqi():
kutilgan = {"ism": "Aziza", "baho": 90, "guruh": "A"}
olingan = {"ism": "Aziza", "baho": 85, "guruh": "A"}
assert olingan == kutilgan
pytest -q test_farq.py
F [100%]
=============================== FAILURES ===============================
___________________________ test_lugat_farqi ___________________________
def test_lugat_farqi():
kutilgan = {"ism": "Aziza", "baho": 90, "guruh": "A"}
olingan = {"ism": "Aziza", "baho": 85, "guruh": "A"}
> assert olingan == kutilgan
E AssertionError: assert {'ism': 'Aziz... 'guruh': 'A'} == {'ism': 'Aziz... 'guruh': 'A'}
E
E Omitting 2 identical items, use -vv to show
E Differing items:
E {'baho': 85} != {'baho': 90}
E Use -v to get more diff
test_farq.py:4: AssertionError
======================= short test summary info ========================
FAILED test_farq.py::test_lugat_farqi - AssertionError: assert {'ism'...
1 failed in 0.01s
Uchta kalitdan faqat farq qilgani ko'rsatildi. Katta obyektlarda bu juda qo'l keladi.
Kasr sonlar tuzog'i #
Endi eng ko'p uchraydigan xatoga o'tamiz:
def test_oddiy_qoshish():
assert 0.1 + 0.2 == 0.3
pytest -q test_kasr.py
F [100%]
=============================== FAILURES ===============================
__________________________ test_oddiy_qoshish __________________________
def test_oddiy_qoshish():
> assert 0.1 + 0.2 == 0.3
E assert (0.1 + 0.2) == 0.3
test_kasr.py:2: AssertionError
======================= short test summary info ========================
FAILED test_kasr.py::test_oddiy_qoshish - assert (0.1 + 0.2) == 0.3
1 failed in 0.01s
Test yiqildi - lekin kodda xato yo'q. E'tibor bering, pytest bu safar
qiymatlarni ochib bermadi: u faqat (0.1 + 0.2) == 0.3 deb yozdi. Sababini
ko'rish uchun yig'indini o'zimiz chop etamiz:
python3 -c "print(0.1 + 0.2)"
0.30000000000000004
Muammo kompyuterning kasr sonlarni saqlash usulida: 0.1 va 0.2 ikkilik
sanoqda aniq ifodalanmaydi, shuning uchun ularning yig'indisi 0.3 dan
mikroskopik farq qiladi.
== bilan solishtirmangBu qoida testlarga emas, umuman barcha dasturlarga tegishli. Pul, o'lchov, foiz - qayerda kasr son bo'lsa, tenglik tekshiruvi ertami-kechmi yiqiladi.
Testlarda yechim - pytest.approx. Oddiy kodda esa butun songa
o'tish (pulni tiyinda saqlash) yoki decimal.Decimal ishlatish.
Yechim - pytest.approx:
import pytest
def test_approx_bilan():
assert 0.1 + 0.2 == pytest.approx(0.3)
def test_royxat_uchun_ham():
assert [0.1 + 0.2, 0.2 + 0.4] == pytest.approx([0.3, 0.6])
def test_aniqlikni_ozi_belgilash():
# 1% gacha farqqa ruxsat
assert 100.4 == pytest.approx(100, rel=0.01)
# mutlaq farq 0.5 dan oshmasin
assert 100.4 == pytest.approx(100, abs=0.5)
pytest -q test_kasr.py
... [100%]
3 passed in 0.01s
| Yozuv | Nimani tekshiradi |
|---|---|
approx(0.3) | Sukut bo'yicha ~0.000001 farqqa ruxsat |
approx(100, rel=0.01) | Nisbiy farq: 1% gacha |
approx(100, abs=0.5) | Mutlaq farq: 0.5 birlikkacha |
approx([1.0, 2.0]) | Ro'yxat ichidagi har bir sonni alohida |
approx({"a": 1.0}) | Lug'at qiymatlarini |
Istisnolarni tekshirish #
Funksiya to'g'ri ishlashini tekshirish - ishning yarmi. Ikkinchi yarmi - u noto'g'ri kirishda to'g'ri xato berishini tekshirish.
Avval funksiyani yozamiz:
def ortacha(baholar):
if not baholar:
raise ValueError("baholar ro'yxati bo'sh")
return sum(baholar) / len(baholar)
Endi istisnoni pytest.raises bilan ushlaymiz:
import pytest
from hisob import ortacha
def test_bosh_royxat_xato_beradi():
with pytest.raises(ValueError):
ortacha([])
def test_xato_matni_ham_tekshiriladi():
with pytest.raises(ValueError, match="bo'sh"):
ortacha([])
def test_xato_obyektini_korish():
with pytest.raises(ValueError) as xato:
ortacha([])
assert "ro'yxati" in str(xato.value)
pytest -q test_istisno.py
... [100%]
3 passed in 0.01s
Uchta usul ham to'g'ri. match= - eng qulayi: u ham istisno turini, ham
xabar matnini bitta qatorda tekshiradi.
pytest.raises ni bo'sh tanaga yozmangQuyidagi test doim o'tadi va hech narsani tekshirmaydi:
with pytest.raises(ValueError):
ortacha([])
assert False # bu qatorga hech qachon yetib kelinmaydi
Birinchi qator istisno tashlagani uchun keyingi qatorlar bajarilmaydi.
Shuning uchun with blokiga faqat bitta chaqiruv yozing, qolgan
tekshiruvlarni blokdan keyin qiling.
Yana bir xavf: match= argumenti doimiy ifoda (regex) sifatida
o'qiladi. match="narx (so'm)" yozsangiz, qavslar guruh sifatida
tushuniladi. Maxsus belgilarni re.escape() bilan himoyalang.
Istisno tashlanmasa nima bo'ladi? Testni ataylab buzib ko'ramiz:
import pytest
from hisob import ortacha
def test_notogri_kutish():
with pytest.raises(ValueError):
ortacha([70, 80])
pytest -q test_istisno.py
F [100%]
=============================== FAILURES ===============================
_________________________ test_notogri_kutish __________________________
def test_notogri_kutish():
> with pytest.raises(ValueError):
^^^^^^^^^^^^^^^^^^^^^^^^^
E Failed: DID NOT RAISE ValueError
test_istisno.py:7: Failed
======================= short test summary info ========================
FAILED test_istisno.py::test_notogri_kutish - Failed: DID NOT RAISE V...
1 failed in 0.01s
DID NOT RAISE - "istisno tashlanmadi" degani. Bu ham foydali xato: demak
kod siz kutgan holatda xato bermayapti.
Xato xabarini o'zingiz yozish #
Ba'zan assert ning avtomatik xabari yetarli bo'lmaydi. Vergul qo'yib,
o'z izohingizni qo'shish mumkin:
from hisob import ortacha
def test_izoh_bilan():
baholar = [45, 50, 55]
natija = ortacha(baholar)
assert natija >= 60, f"guruh o'rtachasi past: {natija:.1f} ({baholar})"
pytest -q test_izoh.py
F [100%]
=============================== FAILURES ===============================
___________________________ test_izoh_bilan ____________________________
def test_izoh_bilan():
baholar = [45, 50, 55]
natija = ortacha(baholar)
> assert natija >= 60, f"guruh o'rtachasi past: {natija:.1f} ({baholar})"
E AssertionError: guruh o'rtachasi past: 50.0 ([45, 50, 55])
E assert 50.0 >= 60
test_izoh.py:7: AssertionError
======================= short test summary info ========================
FAILED test_izoh.py::test_izoh_bilan - AssertionError: guruh o'rtacha...
1 failed in 0.01s
Izoh avtomatik xabardan oldin chiqadi. Bu ayniqsa assert x > 0 kabi
mazmunsiz solishtirishlarda qo'l keladi: assert 50.0 >= 60 qatori xatoning
nima ekanini aytmaydi, izoh esa aytadi.
assert bo'lishi kerakQat'iy qoida yo'q, lekin foydali o'lchov bor: testning nomi bitta gap
bo'lsa, shu gapni isbotlaydigan barcha assertlar joiz.
test_royxatlar ichidagi uchta assert - bitta fikrning uch tomoni,
bu normal. Lekin bitta testda ham o'rtachani, ham xatoni, ham saqlashni
tekshirsangiz - bu uchta alohida test bo'lishi kerak.
Sabab amaliy: birinchi assert yiqilsa, keyingilari umuman
bajarilmaydi. Ya'ni siz bitta ishga tushirishda bitta xato ko'rasiz,
qolganlari yashirin qoladi.
test_solishtirish.pydagi to'rtta testni o'zingizda ishga tushiring.- Ro'yxat testidagi bitta elementni o'zgartiring va farq hisobotini o'qing.
- Lug'at testiga to'rtinchi kalit qo'shing va xato qanday ko'rinishini yozing.
python3 -c "print(0.1 + 0.2)"ni ishga tushirib, chiqqan sonni to'liq yozib oling.- Uni
pytest.approxbilan tuzating. approx(100, rel=0.01)ni101uchun sinab ko'ring - o'tadimi?ortacha([])uchunpytest.raisesbilan test yozing.match=ga mos kelmaydigan matn bering va xatoni o'qing.pytest.raisesblokiga ikkita chaqiruv yozing va nima uchun bu xavfli ekanini tushuntiring.- O'z izohingiz bilan yiqiladigan bitta test yozing va xabarni o'qing.
Xulosa #
- pytest da maxsus tasdiqlash metodlari yo'q -
==,in,>,notyetarli. - Xato xabari turga qarab o'zgaradi: ro'yxatda indeks, lug'atda farq qilgan kalit ko'rsatiladi.
- Kasr sonlarni
==bilan solishtirish ertami-kechmi yiqiladi -0.1 + 0.2 != 0.3. - Sabab - ikkilik sanoqda kasr sonlar aniq ifodalanmasligi; bu Python ning emas, kompyuterning xususiyati.
- Testlarda yechim
pytest.approx, oddiy kodda - butun son yokiDecimal. approxro'yxat va lug'at bilan ham ishlaydi;rel=nisbiy,abs=mutlaq farqni belgilaydi.- Funksiyaning xato holatlari ham tekshirilishi kerak - bu ishning yarmi.
pytest.raises(ValueError, match="matn")bitta qatorda ham turni, ham xabarni tekshiradi.with pytest.raises(...)bloki ichida faqat bitta chaqiruv bo'lsin - qolgan qatorlar bajarilmaydi.match=doimiy ifoda sifatida o'qiladi; maxsus belgilarre.escape()bilan himoyalanadi.- Istisno tashlanmasa, pytest
DID NOT RAISEdeb xabar beradi. assert shart, "izoh"- avtomatik xabarni to'ldiradigan o'z izohingiz.- Bitta testda bir nechta
assertjoiz, lekin ular bitta fikrni isbotlashi kerak.
Keyingi bo'limda katta savolga o'tamiz: umuman nimani testlash kerak va nimani kerak emas.
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.