Mundarija (23)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Misol vs xususiyat
- 2.2. @given va strategiyalar
- 2.3. Strategiyalarni cheklash
- 2.4. Shrinking — eng kichik qarshi misol
- 2.5. Umumiy xususiyat naqshlari
- 2.6. @example — muhim holatni kafolatlash
- 2.7. Qachon property-based
- 2.8. @settings va nazorat
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Property-based asoslari
- Misol 2 — Xatoni topish va shrinking
- Misol 3 — Xususiyat naqshlari va @example
- Misol 4 — Amaliy: pul yaxlitlash funksiyasini sinash
- 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-qism yakuni
17.10-dars: Property-based test (hypothesis)
17-QISM — TESTLASH · 10-dars · 17-qism yakuni
1. Kirish va motivatsiya
Shu paytgacha yozgan barcha testlar misolga asoslangan edi: siz aniq kirish beradingiz (qoshish(2, 3)) va aniq natijani tekshirdingiz (== 5). Bu yaxshi, lekin bitta cheklovi bor: siz faqat o'zingiz o'ylagan holatlarni sinaysiz. Kodingizni buzadigan chegara holati (bo'sh ro'yxat, juda katta son, maxsus belgi) sizning xayolingizga kelmasa, u test qilinmaydi. Property-based testing (xususiyatga asoslangan testlash) buni hal qiladi: siz misol emas, umumiy qoida (xususiyat) yozasiz, kutubxona esa yuzlab tasodifiy kirishni o'zi yaratib, qoidani buzadigan holatni topadi.
hypothesis — Python'ning property-based testing kutubxonasi. U nafaqat xatoni topadi, balki uni eng kichik ko'rinishga keltirib (shrinking) beradi — hatoni tushunishni osonlashtiradi.
Real vaziyat. Bir jamoa summani formatlovchi funksiya yozdi va uni ko'p misol bilan test qildi — hammasi o'tdi. Ular hypothesis qo'shishdi: "har qanday son uchun, formatlangan summani qayta o'qisak, asl songa teng bo'lishi kerak" degan xususiyat yozishdi. hypothesis bu qoidani minglab tasodifiy son bilan sinadi va darhol yiqildi: manfiy nol (-0.0), juda katta son va aniqlik chegarasidagi qiymatlar funksiyani buzardi. Bu holatlar hech kimning xayoliga kelmagan edi. Undan ham yaxshisi — hypothesis xatoni eng kichik ko'rinishga (0.0 va -0.0) qisqartirib berdi, muammoni bir zumda tushunarli qildi.
Bu darsda property-based testing ni — misol emas, xususiyat asosida test yozishni o'rganamiz va 17-qismni yakunlaymiz.
Bu darsda:
- Misolga vs xususiyatga asoslangan test
-
@givenva strategiyalar (strategies) hypothesisyuzlab kirishni o'zi yaratadi- Shrinking — eng kichik qarshi misol
- Umumiy xususiyat naqshlari (roundtrip, invariant, kommutativlik, idempotentlik)
@example— muhim holatni kafolatlash- Qachon property-based, qachon misol
- Amaliy: funksiyani xususiyat bilan sinash
- 17-qism yakuni
ℹ Bu darsda
hypothesis— tashqi kutubxona (pip install hypothesis) ishlatiladi.
2. Nazariya — chuqur tushuntirish
2.1. Misol vs xususiyat
| Misolga asoslangan | Xususiyatga asoslangan |
|---|---|
| Aniq kirish → aniq natija | Umumiy qoida (har kirish uchun) |
qoshish(2, 3) == 5 |
qoshish(a, b) == qoshish(b, a) |
| Siz o'ylagan holatlar | Kutubxona yuzlab holat yaratadi |
| Chegara holatlarini unutish mumkin | Kutubxona ularni topadi |
Ikkalasi bir-birini to'ldiradi: misol testlar aniq holatlarni hujjatlaydi, property testlar kutilmagan chegara holatlarini topadi.
2.2. @given va strategiyalar
@given dekoratori test funksiyasiga tasodifiy kirish beradi; strategiya qanday ma'lumot yaratilishini belgilaydi:
from hypothesis import given, strategies as st
@given(st.integers(), st.integers())
def test_kommutativ(a, b): # a va b — tasodifiy butun sonlar
assert a + b == b + ahypothesis bu testni ko'p marta (sukut 100) turli a, b bilan ishga tushiradi.
| Strategiya | Yaratadi |
|---|---|
st.integers() |
Butun sonlar (istalgan kattalikda) |
st.floats() |
Float (NaN, inf ham) |
st.text() |
Satrlar (Unicode) |
st.lists(st.integers()) |
Butun sonlar ro'yxati |
st.booleans() |
True/False |
st.dictionaries(...) |
Lug'atlar |
st.dates(), st.datetimes() |
Sana/vaqt |
2.3. Strategiyalarni cheklash
st.integers(min_value=1, max_value=100) # 1-100 oralig'ida
st.floats(allow_nan=False, allow_infinity=False) # NaN/inf siz
st.lists(st.integers(), min_size=1) # bo'sh bo'lmagan
st.text(min_size=1, max_size=10) # 1-10 belgili Strategiyalar birlashtiriladi: st.lists(st.tuples(st.text(), st.integers())) — (satr, son) juftliklar ro'yxati.
2.4. Shrinking — eng kichik qarshi misol
hypothesis xatoni topganda, uni eng kichik ko'rinishga qisqartiradi (shrinking):
@given(st.lists(st.integers()))
def test_ortacha(sonlar):
assert ortacha(sonlar) >= 0 # bo'sh ro'yxatda ZeroDivisionErrorhypothesis [] (bo'sh ro'yxat) ni topadi va ko'rsatadi: Falsifying example: sonlar=[]. Katta tasodifiy ro'yxat emas, aynan minimal buzuvchi holat — muammoni tushunish oson.
Shrinking — property-based testing ning eng qimmatli imkoniyati: "1000 elementli tasodifiy ro'yxat yiqildi" emas, "bo'sh ro'yxat yiqildi" deydi.
2.5. Umumiy xususiyat naqshlari
Property yozish uchun "har kirish uchun to'g'ri bo'ladigan qoida" ni topish kerak. Keng tarqalgan naqshlar:
| Naqsh | Misol |
|---|---|
| Roundtrip | decode(encode(x)) == x |
| Invariant | len(sorted(lst)) == len(lst) |
| Kommutativlik | f(a, b) == f(b, a) |
| Idempotentlik | f(f(x)) == f(x) (masalan sorted) |
| Oracle | Sekin, to'g'ri versiya bilan solishtirish |
| Chegaralar | min(lst) <= ortacha(lst) <= max(lst) |
Roundtrip (aylanib qaytish) — eng ko'p ishlatiladigan: seriyalash/deseriyalash (16-qism), kodlash/dekodlash, parse/format.
2.6. @example — muhim holatni kafolatlash
Tasodifiy kirishga qo'shimcha, muayyan holatni har doim sinash uchun:
from hypothesis import given, example, strategies as st
@given(st.integers())
@example(0) # 0 ni har doim sinash (muhim chegara)
@example(-1)
def test_x(n): ... @example — ma'lum muhim/xatoli holatni kafolatli sinaydi (regressiya kabi).
2.7. Qachon property-based
| Property-based mos | Misol afzal |
|---|---|
| Aniq xususiyat bor (roundtrip, invariant) | Aniq biznes qoidasi (soliq(1000) == 120) |
| Ko'p chegara holati | Bitta muayyan holat |
| Algoritmlar, ma'lumot tuzilmalari | Oddiy mantiq |
| Seriyalash, parsing | Konfiguratsiya |
Property-based har joyga mos kelmaydi: ba'zan "to'g'ri natija" ni faqat aniq misol bilan ifodalash mumkin. Ikkalasini birga ishlating.
2.8. @settings va nazorat
from hypothesis import settings
@settings(max_examples=500) # ko'proq kirish sinash
@settings(deadline=None) # vaqt chegarasini olib tashlash
@settings(derandomize=True) # takrorlanuvchi (bir xil kirishlar) derandomize=True — CI va reproduksiya uchun testni deterministik qiladi.
3. Tez ma'lumotnoma
from hypothesis import given, example, settings, strategies as st
@given(st.integers(), st.integers())
def test_kommutativ(a, b):
assert qoshish(a, b) == qoshish(b, a)
@given(st.text())
def test_roundtrip(s):
assert dekod(kod(s)) == s # aylanib qaytish
@given(st.lists(st.integers(), min_size=1))
@example([0]) # muhim holat kafolati
def test_invariant(lst):
assert min(lst) <= max(lst)Qoidalar
misol emas, xususiyat (umumiy qoida) yozing
@given + strategies bilan tasodifiy kirish
roundtrip: decode(encode(x)) == x — eng ko'p ishlatiladi
hypothesis chegara holatlarini o'zi topadi
shrinking — eng kichik qarshi misol
@example — muhim holatni kafolatlash
misol va property testlarni birga ishlating4. Batafsil misollar
Misollarda
pytest(hypothesis bilan) ni deterministik ishga tushiruvchipytest_ishgayordamchisi ishlatiladi (derandomize=True).
Misol 1 — Property-based asoslari
"""@given + strategiyalar; hypothesis yuzlab kirishni sinaydi; kommutativlik va roundtrip xususiyatlari; strategiyalarni cheklash."""
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 = '''
from hypothesis import given, settings, strategies as st
@settings(derandomize=True)
@given(st.integers(), st.integers())
def test_qoshish_kommutativ(a, b):
assert a + b == b + a
@settings(derandomize=True)
@given(st.lists(st.integers()))
def test_reverse_roundtrip(lst):
# ikki marta teskari — asl ro'yxat (roundtrip)
assert list(reversed(list(reversed(lst)))) == lst
@settings(derandomize=True)
@given(st.text())
def test_encode_decode_roundtrip(s):
# utf-8 ga o'girib qaytarsak — asl satr
assert s.encode("utf-8").decode("utf-8") == s
@settings(derandomize=True)
@given(st.lists(st.integers(), min_size=1))
def test_sorted_invariant(lst):
saralangan = sorted(lst)
assert len(saralangan) == len(lst) # uzunlik saqlanadi
assert min(lst) == saralangan[0] # eng kichik birinchi
assert max(lst) == saralangan[-1] # eng katta oxirgi
@settings(derandomize=True)
@given(st.integers(min_value=1, max_value=100))
def test_cheklangan_oralik(n):
assert 1 <= n <= 100 # strategiya cheklovi
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "test_prop.py").write_text(TEST, encoding="utf-8")
print("=== 1. Property-based testlar ===")
kod, satrlar = pytest_ishga(r, "-v", "--tb=short")
for satr in satrlar:
if "::" in satr:
print(f" {satr}")
print(f" chiqish kodi: {kod}")
print(" ⭐ har test yuzlab tasodifiy kirish bilan sinaldi va o'tdi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Property-based testlar ===
test_prop.py::test_qoshish_kommutativ PASSED
test_prop.py::test_reverse_roundtrip PASSED
test_prop.py::test_encode_decode_roundtrip PASSED
test_prop.py::test_sorted_invariant PASSED
test_prop.py::test_cheklangan_oralik PASSED
chiqish kodi: 0
⭐ har test yuzlab tasodifiy kirish bilan sinaldi va o'tdiNima ko'rsatdi: 2.1, 2.2, 2.3, 2.5-bo'limlar.
Misol 2 — Xatoni topish va shrinking
"""hypothesis o'ylanmagan chegara holatini topadi (bo'sh ro'yxat, maxsus belgi); shrinking bilan eng kichik qarshi misolga qisqartiradi."""
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")
natija = []
for l in r.stdout.splitlines():
if re.match(r"(=+ test session|collecting|collected|platform |cachedir|rootdir|plugins|configfile|hypothesis profile|Traceback|^\s+File |^\s+\^|^\s+[a-z_]+\.py:)", 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)
l = l.replace("E ", " ").rstrip()
natija.append(l)
return r.returncode, natija
KOD = '''
def ortacha(sonlar):
return sum(sonlar) / len(sonlar) # bo'sh ro'yxatda XATO
def birinchi_harf_katta(matn):
return matn[0].upper() + matn[1:] # bo'sh satrda XATO
'''
TEST = '''
from hypothesis import given, settings, strategies as st
from kod import ortacha, birinchi_harf_katta
@settings(derandomize=True)
@given(st.lists(st.integers()))
def test_ortacha_xato(sonlar):
# xususiyat: natija son bo'lishi kerak — lekin bo'sh ro'yxatda yiqiladi
natija = ortacha(sonlar)
assert isinstance(natija, float)
@settings(derandomize=True)
@given(st.text())
def test_bosh_satr_xato(matn):
# xususiyat: har satr uchun ishlashi kerak — lekin bo'sh satrda yiqiladi
natija = birinchi_harf_katta(matn)
assert len(natija) == len(matn)
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "kod.py").write_text(KOD, encoding="utf-8")
(r / "test_xato.py").write_text(TEST, encoding="utf-8")
print("=== 1. hypothesis chegara holatini topadi ===")
kod, satrlar = pytest_ishga(r, "--tb=short", "-q")
for satr in satrlar:
s = satr.strip()
if s.startswith("FAILED") or "Falsifying" in s or "Failing test case" in s \
or "sonlar=" in s or "matn=" in s or "passed" in s or "failed" in s \
or "ZeroDivision" in s or "IndexError" in s:
print(f" {s}")
print(f" chiqish kodi: {kod}")
print(" ⭐ shrinking: eng kichik qarshi misol — bo'sh ro'yxat [] va bo'sh satr ''")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. hypothesis chegara holatini topadi ===
ZeroDivisionError: division by zero
Failing test case: test_ortacha_xato(
sonlar=[],
IndexError: string index out of range
Failing test case: test_bosh_satr_xato(
matn='',
FAILED test_xato.py::test_ortacha_xato - ZeroDivisionError: division by zero
FAILED test_xato.py::test_bosh_satr_xato - IndexError: string index out of range
2 failed
chiqish kodi: 1
⭐ shrinking: eng kichik qarshi misol — bo'sh ro'yxat [] va bo'sh satr ''Nima ko'rsatdi: 2.4-bo'lim.
Misol 3 — Xususiyat naqshlari va @example
"""umumiy naqshlar: roundtrip (JSON), idempotentlik (sorted), oracle (sekin/tez versiya); @example bilan muhim holatni kafolatlash."""
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 json
from hypothesis import given, example, settings, strategies as st
# 1. Roundtrip — JSON seriyalash 16.2-bob
@settings(derandomize=True)
@given(st.dictionaries(st.text(), st.integers()))
def test_json_roundtrip(d):
assert json.loads(json.dumps(d)) == d
# 2. Idempotentlik — ikki marta saralash bir marta bilan bir xil
@settings(derandomize=True)
@given(st.lists(st.integers()))
def test_sorted_idempotent(lst):
assert sorted(sorted(lst)) == sorted(lst)
# 3. Oracle — o'z implementatsiya vs to'g'ri versiya
def mening_maks(lst):
natija = lst[0]
for x in lst[1:]:
if x > natija:
natija = x
return natija
@settings(derandomize=True)
@given(st.lists(st.integers(), min_size=1))
def test_maks_oracle(lst):
assert mening_maks(lst) == max(lst) # built-in max bilan solishtirish
# 4. @example bilan muhim holatlar
@settings(derandomize=True)
@given(st.integers())
@example(0)
@example(-1)
@example(2**63)
def test_kvadrat_manfiy_emas(n):
assert n * n >= 0
'''
def main() -> None:
with tempfile.TemporaryDirectory() as katalog:
r = Path(katalog)
(r / "test_naqsh.py").write_text(TEST, encoding="utf-8")
print("=== 1. Xususiyat naqshlari ===")
kod, satrlar = pytest_ishga(r, "-v", "--tb=short")
for satr in satrlar:
if "::" in satr:
print(f" {satr}")
print(f" chiqish kodi: {kod}")
print(" ⭐ roundtrip, idempotentlik, oracle, @example — barchasi o'tdi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Xususiyat naqshlari ===
test_naqsh.py::test_json_roundtrip PASSED
test_naqsh.py::test_sorted_idempotent PASSED
test_naqsh.py::test_maks_oracle PASSED
test_naqsh.py::test_kvadrat_manfiy_emas PASSED
chiqish kodi: 0
⭐ roundtrip, idempotentlik, oracle, @example — barchasi o'tdiNima ko'rsatdi: 2.5, 2.6-bo'limlar.
Misol 4 — Amaliy: pul yaxlitlash funksiyasini sinash
Pul summasini tiyingacha yaxlitlovchi funksiyani (16.2, Decimal) property-based va misolga asoslangan test bilan birga sinaymiz. Property testlar chegara holatlarini (manfiy, nol, juda katta, aniqlik chegarasi) topadi; misol testlar muayyan biznes qoidalarini hujjatlaydi. Bu — ikki yondashuvni birlashtirishning namunasi.
"""pul yaxlitlash: property testlar (roundtrip, invariant, chegaralar) + misol testlar (aniq biznes qoidalari); ikki yondashuv birga."""
import re
import subprocess
import sys
import tempfile
from pathlib import Path
KOD = '''
from decimal import Decimal, ROUND_HALF_UP
def tiyinga_yaxlitla(summa: Decimal) -> Decimal:
"""Summani 2 xona (tiyin) aniqligigacha yaxlitlaydi."""
return summa.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
def foizni_qolla(summa: Decimal, foiz: Decimal) -> Decimal:
"""Foiz qo'llab, tiyinga yaxlitlaydi."""
return tiyinga_yaxlitla(summa * (Decimal(1) + foiz / Decimal(100)))
'''
TEST = '''
from decimal import Decimal
from hypothesis import given, example, settings, strategies as st
from pul import tiyinga_yaxlitla, foizni_qolla
# Strategiya: mantiqli pul summalari
pul = st.decimals(min_value=Decimal("0"), max_value=Decimal("1000000"),
allow_nan=False, allow_infinity=False, places=4)
# --- Property testlar ---
@settings(derandomize=True)
@given(pul)
def test_yaxlitlash_idempotent(summa):
# ikki marta yaxlitlash — bir marta bilan bir xil
bir = tiyinga_yaxlitla(summa)
assert tiyinga_yaxlitla(bir) == bir
@settings(derandomize=True)
@given(pul)
def test_natija_ikki_xona(summa):
# natija har doim 2 xonali (tiyin)
natija = tiyinga_yaxlitla(summa)
assert -natija.as_tuple().exponent == 2
@settings(derandomize=True)
@given(pul)
def test_yaxlitlash_yaqin(summa):
# yaxlitlangan summa asldan 0.005 dan uzoq bo'lmasin
natija = tiyinga_yaxlitla(summa)
assert abs(natija - summa) <= Decimal("0.005")
@settings(derandomize=True)
@given(pul)
def test_nol_foiz_ozgarmas(summa):
# 0% foiz summani o'zgartirmaydi (yaxlitlangan holda)
assert foizni_qolla(summa, Decimal(0)) == tiyinga_yaxlitla(summa)
# --- Misol testlar (aniq biznes qoidalari) ---
def test_yaxlitlash_yuqoriga():
assert tiyinga_yaxlitla(Decimal("10.125")) == Decimal("10.13")
def test_yaxlitlash_pastga():
assert tiyinga_yaxlitla(Decimal("10.124")) == Decimal("10.12")
def test_foiz_qollash():
assert foizni_qolla(Decimal("100"), Decimal("12")) == Decimal("112.00")
def test_soliq_hisobi():
# 12% QQS: 1250.50 → 1400.56
assert foizni_qolla(Decimal("1250.50"), Decimal("12")) == Decimal("1400.56")
'''
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 / "pul.py").write_text(KOD, encoding="utf-8")
(r / "test_pul.py").write_text(TEST, encoding="utf-8")
print("=== 1. Property + misol testlar birga ===")
kod, satrlar = pytest_ishga(r, "-v", "--tb=short")
for satr in satrlar:
if "::" in satr:
print(f" {satr}")
print(f"\n chiqish kodi: {kod}")
print(" ⭐ property — chegara holatlari; misol — aniq biznes qoidalari")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Property + misol testlar birga ===
test_pul.py::test_yaxlitlash_idempotent PASSED
test_pul.py::test_natija_ikki_xona PASSED
test_pul.py::test_yaxlitlash_yaqin PASSED
test_pul.py::test_nol_foiz_ozgarmas PASSED
test_pul.py::test_yaxlitlash_yuqoriga PASSED
test_pul.py::test_yaxlitlash_pastga PASSED
test_pul.py::test_foiz_qollash PASSED
test_pul.py::test_soliq_hisobi PASSED
chiqish kodi: 0
⭐ property — chegara holatlari; misol — aniq biznes qoidalariNima ko'rsatdi: 2.5, 2.7-bo'limlar.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Property test misol testni almashtiradi" | Bir-birini to'ldiradi |
| "Property test — bir kirish bilan" | Yuzlab tasodifiy kirish |
| "Yiqilsa, katta tasodifiy kirish ko'rinadi" | Shrinking — eng kichik qarshi misol |
| "Har kod uchun property yozish oson" | Ba'zan misol tabiiyroq |
| "hypothesis sekin" | Sukut 100 kirish — tez |
"@example keraksiz" |
Muhim holatlarni kafolatlaydi |
| "Property test — faqat matematik kod uchun" | Roundtrip, invariant — hamma joyda |
| "hypothesis tasodifiy — takrorlanmaydi" | derandomize=True bilan deterministik |
6. Keng tarqalgan xatolar va yechimlari
1. Faqat misol testlar (chegara holatlari o'tkaziladi)
def test_ortacha(): assert ortacha([1, 2, 3]) == 2 # ❌ bo'sh ro'yxat?
@given(st.lists(st.integers())) # ✅ hypothesis topadi
def test_ortacha_prop(lst): ...2. Xususiyat o'rniga aniq natijani kutish
@given(st.integers())
def test_x(n): assert kvadrat(n) == 25 # ❌ har n uchun 25 emas
def test_x(n): assert kvadrat(n) == n * n # ✅ umumiy qoida3. NaN/inf ni hisobga olmaslik
@given(st.floats()) # ❌ NaN test yiqiladi
def test_x(f): assert f == f
@given(st.floats(allow_nan=False)) # ✅
def test_x(f): assert f == f4. Juda keng strategiya
@given(st.integers()) # ❌ juda katta sonlar
def test_massiv(n): arr = [0] * n # xotira portlashi
@given(st.integers(min_value=0, max_value=1000)) # ✅ cheklangan
def test_massiv(n): ...5. Property da yon ta'sir
@given(st.integers())
def test_x(n): global hisob; hisob += n # ❌ holat to'planadi
# ✅ property sof bo'lsin (yon ta'sirsiz)6. Muhim holatni kafolatlamaslik
@given(st.integers())
def test_x(n): ... # 0 sinalmasligi mumkin
@given(st.integers())
@example(0) # ✅ 0 kafolatlanadi
def test_x(n): ...7. Deterministik emas (CI da beqaror)
@given(st.integers()) # ⚠️ har run boshqa kirish
@settings(derandomize=True) # ✅ takrorlanuvchi
@given(st.integers())
def test_x(n): ...7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 16.2-dars (o'tilgan): JSON — roundtrip xususiyati (
loads(dumps(x)) == x) - 17.2–17.4-darslar (o'tilgan): pytest, parametrize — hypothesis ular bilan ishlaydi
- 17.7-dars (o'tilgan): coverage — property testlar qamrovni oshiradi
- 24-qism: ma'lumot tahlili — funksiyalarni property bilan sinash
- 27-qism: arxitektura — invariantlarni tekshirish
8. Eng yaxshi amaliyotlar
Misol emas, xususiyat (umumiy qoida) yozing.
Roundtrip (
decode(encode(x)) == x) — eng ko'p ishlatiladigan naqsh.Strategiyalarni mantiqli cheklang (
min_value,allow_nan=False).Shrinking yordamida eng kichik qarshi misolni o'rganing.
Muhim/chegara holatlarni
@examplebilan kafolatlang.Property va misol testlarni birga ishlating.
CI uchun
derandomize=Truebilan takrorlanuvchi qiling.Property sof (yon ta'sirsiz) bo'lsin.
9. Amaliy topshiriq
Vazifa 1: Xususiyatni aniqlang
Har funksiya uchun property (umumiy qoida) yozing:
1. qoshish(a, b) # kommutativlik?
2. reversed(reversed(lst)) # roundtrip?
3. sorted(lst) # idempotentlik? invariant?
4. abs(x) # har doim?
5. json.dumps/loads # roundtrip?
6. len(lst + [x]) # invariant?
7. max(lst) # chegara?
8. str(int(s)) # qachon roundtrip?Javoblar
qoshish(a, b) == qoshish(b, a)(kommutativlik)list(reversed(list(reversed(lst)))) == lst(roundtrip)sorted(sorted(lst)) == sorted(lst)(idempotentlik);len(sorted(lst)) == len(lst)(invariant)abs(x) >= 0(har doim manfiy emas)json.loads(json.dumps(x)) == x(roundtrip, oddiy turlarda)len(lst + [x]) == len(lst) + 1(invariant)max(lst) in lstvamax(lst) >= min(lst)(chegara)str(int(s)) == sfaqatsnormallashgan butun son satri bo'lsa (masalan"007"emas)
Vazifa 2: Xatolarni tuzating
1. @given(st.integers())
def test_kvadrat(n):
assert kvadrat(n) == 4 # har n uchun 4 emas
2. @given(st.floats())
def test_teng(f):
assert f == f # NaN != NaN
3. @given(st.integers())
def test_massiv(n):
arr = list(range(n)) # katta n — xotira
4. @given(st.text())
def test_birinchi(s):
assert s[0].isupper() or True # bo'sh satr — IndexError
5. @given(st.integers())
def test_hisob(n):
global jami
jami += n # holat to'planadiJavoblar
1. @given(st.integers())
def test_kvadrat(n):
assert kvadrat(n) == n * n # umumiy qoida
2. @given(st.floats(allow_nan=False)) # NaN siz
def test_teng(f):
assert f == f
3. @given(st.integers(min_value=0, max_value=1000)) # cheklangan
def test_massiv(n):
arr = list(range(n))
4. @given(st.text(min_size=1)) # bo'sh bo'lmagan
def test_birinchi(s):
assert isinstance(s[0], str)
5. @given(st.integers())
def test_hisob(n):
assert hisob([n]) == n # sof — global holatsizVazifa 3: Roundtrip to'plami
16-qismdagi formatlar uchun roundtrip property testlari:
- JSON:
loads(dumps(x)) == x(oddiy turlar strategiyasi) - CSV: bir qatorli yozib-o'qish teng
- Base64:
decode(encode(x)) == x - Har biriga qaysi turlar ishlamasligini toping (
hypothesisbilan)
Vazifa 4: Saralash algoritmini sinash
O'z saralash funksiyangizni (masalan bubble sort):
- Invariant: uzunlik saqlanadi, elementlar bir xil (
Counter) - Oracle: built-in
sortedbilan solishtiring - Idempotentlik: ikki marta saralash
hypothesistopgan xatolarni tuzating
Vazifa 5: Holatlar mashinasi
hypothesis ning RuleBasedStateMachine bilan (Stack yoki Savat):
- Amallar (push/pop) ketma-ketligini tasodifiy yarating
- Invariantlarni har amaldan keyin tekshiring
hypothesisbuzuvchi ketma-ketlikni topsin- Oddiy property test bilan solishtiring
Vazifa 6: Shrinking ni kuzatish
- Ataylab xatoli funksiya yozing (masalan
n > 100da yiqiladi) hypothesistopgan qarshi misolni ko'ring- Shrinking qanday minimal qiymatga (
101) qisqartirganini kuzating @examplebilan uni regressiya testiga aylantiring
Vazifa 7: O'ylash
Property-based testing 1999-yilda Haskell'ning QuickCheck kutubxonasidan boshlangan va endi ko'p tilda (Python hypothesis, JS fast-check) mavjud. Shunga qaramay, u misolga asoslangan testdan kamroq tarqalgan. Nima uchun kuchli g'oya bo'lsa ham property-based testing sekin qabul qilinmoqda, va u qanday muammolarni misol testlar hal qila olmaydigan darajada yaxshi hal qiladi?
Javob
Qisqa javob: Property-based testing sekin tarqalyapti, chunki u boshqacha fikrlashni talab qiladi: aniq misol ("2+3=5") o'rniga umumiy xususiyat ("qo'shish kommutativ") topish kerak, bu esa qiyinroq va o'rgatish talab qiladi. Ko'p dasturchi misol testlarni tabiiy deb biladi. Lekin property testing misol testlar printsipial hal qila olmaydigan narsani hal qiladi: siz o'ylamagan chegara holatlarini avtomatik topish. Inson faqat o'zi tasavvur qilgan holatlarni sinaydi; hypothesis minglab holatni yaratib, tasavvurni kengaytiradi.
1. Nega sekin tarqalyapti
| Sabab | Tafsilot |
|---|---|
| Fikrlash o'zgarishi | Misol emas, xususiyat topish |
| O'rganish egri chizig'i | Strategiyalar, naqshlar |
| "Xususiyat topib bo'lmaydi" tuyg'usi | Ba'zi kod uchun qiyin |
| Misol testlar tabiiyroq | "2+3=5" — aniq va tushunarli |
| Vaqt bosimi | Boshda ko'proq o'ylash |
2. Property testing hal qiladigan noyob muammolar
| Muammo | Nega misol test yeta olmaydi |
|---|---|
| O'ylanmagan chegara holatlari | Inson faqat tasavvurini sinaydi |
| Kombinatsiyalar portlashi | Qo'lda yozib bo'lmaydi |
| Minimal qarshi misol (shrinking) | Qo'lda topish qiyin |
| Regressiya (avtomatik) | Yangi holatlar o'zi topiladi |
| Invariantlarni ifodalash | Umumiy qoida — kuchli hujjat |
3. Ikkalasining o'rni
| Misol test | Property test |
|---|---|
| Aniq biznes qoidasi | Umumiy xususiyat |
| Hujjat/spetsifikatsiya | Chegara holatlarini topish |
| Tushunarli, aniq | Kutilmaganni ochadi |
| Har kod uchun | Xususiyat aniq bo'lganda |
4. Nega baribir qimmatli
Property testing "test yozuvchi sifatida siz o'ylamagan narsani" topadi — bu uning eng katta qiymati. 0.1 + 0.2, bo'sh ro'yxat, -0.0, maxsus Unicode belgilar — bularni inson ko'pincha unutadi, hypothesis esa topadi.
5. Amaliy tavsiya
- Aniq xususiyat bor bo'lsa (roundtrip, invariant) — property test
- Aniq biznes qoidasi — misol test
- Ikkalasini birga: property chegaralarni, misol qoidalarni
- Kritik kod (pul, xavfsizlik, algoritm) — property test ayniqsa qimmatli
6. Xulosa
- Sekin tarqalishi — fikrlash o'zgarishi va o'rganish talabidan
- O'ylanmagan chegara holatlarini topish — noyob qiymati
- Shrinking — minimal qarshi misol
- Misol va property testlar birga eng kuchli
Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.
Xulosa
Bu darsda property-based testing ni — misol emas, xususiyat asosida test yozishni o'rgandik.
Eng muhim uch fikr:
Misol emas, xususiyat. Misolga asoslangan test siz o'ylagan holatlarni sinaydi; property-based test umumiy qoida (
qoshish(a, b) == qoshish(b, a)) yozadi vahypothesisyuzlab tasodifiy kirishni o'zi yaratib, qoidani buzadigan holatni topadi.@givenva strategiyalar (st.integers(),st.text(),st.lists(...)) kirishni belgilaydi. Bu misol testlar o'tkazib yuboradigan chegara holatlarini (bo'sh ro'yxat,-0.0, maxsus belgi) topadi.Shrinking va xususiyat naqshlari.
hypothesisxatoni topganda uni eng kichik qarshi misolga qisqartiradi (Falsifying example: sonlar=[]) — "1000 elementli tasodifiy ro'yxat" emas, aynan minimal buzuvchi holat, muammoni tushunish oson. Umumiy naqshlar: roundtrip (decode(encode(x)) == x— eng ko'p ishlatiladigan), invariant (len(sorted(lst)) == len(lst)), idempotentlik (sorted(sorted(x)) == sorted(x)), oracle (to'g'ri versiya bilan solishtirish).@examplemuhim holatni kafolatlaydi.Misol va property testlarni birga. Property-based testing misol testni almashtirmaydi — to'ldiradi: property testlar chegara holatlarini avtomatik topadi, misol testlar aniq biznes qoidalarini (
soliq(1250.50, 12) == 1400.56) hujjatlaydi. Aniq xususiyat bor bo'lganda (algoritmlar, seriyalash, pul hisobi) property test ayniqsa kuchli;derandomize=Truebilan CI uchun takrorlanuvchi qiling.
Bu bilan 17-qism — Testlash yakunlandi. Keyingi 18-qismda kod sifati va vositalari ga o'tamiz: virtual muhit (venv), bog'liqliklarni boshqarish (pip, poetry, uv), lint (ruff), formatlash (black), pre-commit, profiling va debugging.
17-qism yakuni
Testlash qismida kodning to'g'ri ishlashini kafolatlaydigan barcha asosiy vositalarni o'rgandik — misoldan xususiyatgacha, unit'dan integratsiongacha.
| Dars | Mavzu | Asosiy g'oya |
|---|---|---|
| 17.1 | Nega test yozamiz | Qo'lda emas, avtomatik; regressiya |
| 17.2 | pytest asoslari | Oddiy assert, raises, approx, belgilar |
| 17.3 | Fixture | Tayyorlash/tozalash, yield, conftest.py |
| 17.4 | parametrize | Sinov holatlari — ma'lumot sifatida |
| 17.5 | Mock — tushuncha | Tashqi bog'liqlikdan izolyatsiya |
| 17.6 | Mock — amaliyot | patch, "qayerda ishlatilsa" |
| 17.7 | Coverage | Ko'r nuqtalar; 100% ≠ xatosiz |
| 17.8 | TDD | Test avval; qizil-yashil-refaktoring |
| 17.9 | Integratsion | Qismlar birga; test piramidasi |
| 17.10 | Property-based | Xususiyat; shrinking; hypothesis |
Umumiy tamoyillar:
- Test — investitsiya: boshda vaqt, keyin ishonch va tezlik. Har tuzatilgan xato uchun regressiya testi.
- Piramida: ko'p tez unit, o'rta integratsion, kam E2E.
- Ma'noli testlar: coverage foizini emas, muhim yo'llarni va xato holatlarini qamrang.
- To'g'ri vosita: tashqi bog'liqlik — mock; o'z baza — haqiqiy; umumiy xususiyat — property test.
Keyingi bosqich — kod sifati va vositalari (18): muhit, bog'liqlik, lint, format, profiling — professional dasturchining kundalik asboblar to'plami.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!