పైథాన్ హైపోథెసిస్ లైబ్రరీతో ప్రాపర్టీ-బేస్డ్ టెస్టింగ్ను కనుగొనండి. ఎడ్జ్ కేసులను కనుగొని, మరింత పటిష్టమైన, విశ్వసనీయమైన సాఫ్ట్వేర్ను రూపొందించడానికి ఉదాహరణ-ఆధారిత పరీక్షలను దాటి ముందుకు సాగండి.
యూనిట్ టెస్ట్లకు అతీతంగా: పైథాన్ హైపోథెసిస్తో ప్రాపర్టీ-బేస్డ్ టెస్టింగ్పై లోతైన విశ్లేషణ
సాఫ్ట్వేర్ డెవలప్మెంట్ ప్రపంచంలో, నాణ్యతకు టెస్టింగ్ పునాది లాంటిది. దశాబ్దాలుగా, ఉదాహరణ-ఆధారిత టెస్టింగ్ ప్రధాన పద్ధతిగా ఉంది. మేము ఇన్పుట్లను చాలా జాగ్రత్తగా రూపొందించి, ఆశించిన అవుట్పుట్లను నిర్వచించి, మా కోడ్ అనుకున్న విధంగా ప్రవర్తిస్తుందో లేదో ధృవీకరించడానికి అసర్షన్లు వ్రాస్తాము. unittest
మరియు pytest
వంటి ఫ్రేమ్వర్క్లలో కనిపించే ఈ విధానం శక్తివంతమైనది మరియు అవసరమైనది. కానీ మీరు వెతకాలని కూడా అనుకోని బగ్స్ను కనుగొనగల ఒక పరిపూరకరమైన విధానం ఉందని నేను మీకు చెబితే?
ప్రాపర్టీ-బేస్డ్ టెస్టింగ్ ప్రపంచానికి స్వాగతం. ఇది నిర్దిష్ట ఉదాహరణలను పరీక్షించడం నుండి మీ కోడ్ యొక్క సాధారణ లక్షణాలను ధృవీకరించడంపై దృష్టిని మారుస్తుంది. మరియు పైథాన్ ఎకోసిస్టమ్లో, ఈ విధానంలో తిరుగులేని ఛాంపియన్ హైపోథెసిస్ అనే లైబ్రరీ.
ఈ సమగ్ర గైడ్ మిమ్మల్ని ఒక పూర్తి బిగినర్ నుండి హైపోథెసిస్తో ప్రాపర్టీ-బేస్డ్ టెస్టింగ్లో నమ్మకమైన అభ్యాసకుడిగా మారుస్తుంది. మేము ప్రధాన భావనలను అన్వేషిస్తాము, ఆచరణాత్మక ఉదాహరణలలోకి లోతుగా వెళ్తాము మరియు మరింత పటిష్టమైన, విశ్వసనీయమైన మరియు బగ్-నిరోధక సాఫ్ట్వేర్ను రూపొందించడానికి ఈ శక్తివంతమైన సాధనాన్ని మీ రోజువారీ డెవలప్మెంట్ వర్క్ఫ్లోలో ఎలా ఏకీకృతం చేయాలో నేర్చుకుంటాము.
ప్రాపర్టీ-బేస్డ్ టెస్టింగ్ అంటే ఏమిటి? ఒక ఆలోచనా విధానంలో మార్పు
హైపోథెసిస్ను అర్థం చేసుకోవడానికి, మనం మొదట ప్రాపర్టీ-బేస్డ్ టెస్టింగ్ యొక్క ప్రాథమిక ఆలోచనను గ్రహించాలి. మనందరికీ తెలిసిన సాంప్రదాయ ఉదాహరణ-ఆధారిత టెస్టింగ్తో పోల్చి చూద్దాం.
ఉదాహరణ-ఆధారిత టెస్టింగ్: సుపరిచితమైన మార్గం
మీరు ఒక కస్టమ్ సార్టింగ్ ఫంక్షన్, my_sort()
వ్రాశారని ఊహించుకోండి. ఉదాహరణ-ఆధారిత టెస్టింగ్తో, మీ ఆలోచనా విధానం ఇలా ఉంటుంది:
- "దీనిని ఒక సాధారణ, క్రమబద్ధమైన జాబితాతో పరీక్షిద్దాం." ->
assert my_sort([1, 2, 3]) == [1, 2, 3]
- "రివర్స్-ఆర్డర్డ్ జాబితా అయితే?" ->
assert my_sort([3, 2, 1]) == [1, 2, 3]
- "ఖాళీ జాబితా అయితే?" ->
assert my_sort([]) == []
- "డూప్లికేట్లతో ఉన్న జాబితా?" ->
assert my_sort([5, 1, 5, 2]) == [1, 2, 5, 5]
- "మరియు నెగటివ్ సంఖ్యలతో ఉన్న జాబితా?" ->
assert my_sort([-1, -5, 0]) == [-5, -1, 0]
ఇది ప్రభావవంతంగా ఉంటుంది, కానీ దీనికి ఒక ప్రాథమిక పరిమితి ఉంది: మీరు ఆలోచించగలిగే కేసులను మాత్రమే మీరు పరీక్షిస్తున్నారు. మీ పరీక్షలు మీ ఊహకు తగ్గట్టుగా మాత్రమే ఉంటాయి. మీరు చాలా పెద్ద సంఖ్యలు, ఫ్లోటింగ్-పాయింట్ తప్పులు, నిర్దిష్ట యూనికోడ్ అక్షరాలు లేదా ఊహించని ప్రవర్తనకు దారితీసే సంక్లిష్ట డేటా కలయికలను కలిగి ఉన్న ఎడ్జ్ కేసులను మీరు కోల్పోవచ్చు.
ప్రాపర్టీ-బేస్డ్ టెస్టింగ్: స్థిరమైన నియమాలలో ఆలోచించడం
ప్రాపర్టీ-బేస్డ్ టెస్టింగ్ ఈ పద్ధతిని తలక్రిందులు చేస్తుంది. నిర్దిష్ట ఉదాహరణలను అందించడానికి బదులుగా, మీరు మీ ఫంక్షన్ యొక్క లక్షణాలను, లేదా అంతర్లీన నియమాలను నిర్వచిస్తారు—ఇవి ఏదైనా చెల్లుబాటు అయ్యే ఇన్పుట్ కోసం నిజం కావాల్సిన నియమాలు. మన my_sort()
ఫంక్షన్ కోసం, ఈ లక్షణాలు ఇలా ఉండవచ్చు:
- అవుట్పుట్ క్రమబద్ధంగా ఉంటుంది: సంఖ్యల యొక్క ఏదైనా జాబితా కోసం, అవుట్పుట్ జాబితాలోని ప్రతి మూలకం దాని తర్వాతి మూలకం కంటే తక్కువ లేదా సమానంగా ఉంటుంది.
- అవుట్పుట్ ఇన్పుట్లోని అవే మూలకాలను కలిగి ఉంటుంది: క్రమబద్ధీకరించబడిన జాబితా అసలు జాబితా యొక్క ఒక మార్పు మాత్రమే; ఏ మూలకాలు జోడించబడవు లేదా కోల్పోబడవు.
- ఫంక్షన్ ఐడెంపోటెంట్: ఇప్పటికే క్రమబద్ధీకరించబడిన జాబితాను క్రమబద్ధీకరించడం దానిని మార్చకూడదు. అంటే,
my_sort(my_sort(some_list)) == my_sort(some_list)
.
ఈ విధానంతో, మీరు టెస్ట్ డేటాను వ్రాయడం లేదు. మీరు నియమాలను వ్రాస్తున్నారు. ఆపై మీరు హైపోథెసిస్ వంటి ఫ్రేమ్వర్క్కు వందల లేదా వేలకొద్దీ యాదృచ్ఛిక, వైవిధ్యమైన, మరియు తరచుగా మోసపూరిత ఇన్పుట్లను ఉత్పత్తి చేసి, మీ లక్షణాలను తప్పు అని నిరూపించడానికి ప్రయత్నించేలా చేస్తారు. అది ఒక లక్షణాన్ని ఉల్లంఘించే ఇన్పుట్ను కనుగొంటే, అది ఒక బగ్ను కనుగొన్నట్లే.
హైపోథెసిస్ పరిచయం: మీ ఆటోమేటెడ్ టెస్ట్ డేటా జనరేటర్
హైపోథెసిస్ అనేది పైథాన్ కోసం ప్రీమియర్ ప్రాపర్టీ-బేస్డ్ టెస్టింగ్ లైబ్రరీ. ఇది మీరు నిర్వచించిన లక్షణాలను తీసుకుని, వాటిని సవాలు చేయడానికి టెస్ట్ డేటాను ఉత్పత్తి చేసే కష్టమైన పనిని చేస్తుంది. ఇది కేవలం యాదృచ్ఛిక డేటా జనరేటర్ కాదు; ఇది బగ్స్ను సమర్థవంతంగా కనుగొనడానికి రూపొందించబడిన ఒక తెలివైన మరియు శక్తివంతమైన సాధనం.
హైపోథెసిస్ యొక్క ముఖ్య లక్షణాలు
- ఆటోమేటిక్ టెస్ట్ కేస్ జనరేషన్: మీకు అవసరమైన డేటా యొక్క *ఆకృతిని* మీరు నిర్వచిస్తారు (ఉదా., "పూర్ణాంకాల జాబితా," "కేవలం అక్షరాలను కలిగి ఉన్న స్ట్రింగ్," "భవిష్యత్తులోని ఒక డేట్టైమ్"), మరియు హైపోథెసిస్ ఆ ఆకృతికి అనుగుణంగా అనేక రకాల ఉదాహరణలను ఉత్పత్తి చేస్తుంది.
- తెలివైన సంకోచం (Intelligent Shrinking): ఇది ఒక మ్యాజిక్ ఫీచర్. హైపోథెసిస్ ఒక విఫలమైన టెస్ట్ కేసును కనుగొన్నప్పుడు (ఉదాహరణకు, మీ సార్ట్ ఫంక్షన్ను క్రాష్ చేసే 50 సంక్లిష్ట సంఖ్యల జాబితా), అది కేవలం ఆ భారీ జాబితాను నివేదించదు. ఇది తెలివిగా మరియు స్వయంచాలకంగా ఇన్పుట్ను సరళీకృతం చేసి, వైఫల్యానికి కారణమయ్యే అతి చిన్న ఉదాహరణను కనుగొంటుంది. 50-ఎలిమెంట్ల జాబితాకు బదులుగా, కేవలం
[inf, nan]
తో వైఫల్యం సంభవిస్తుందని ఇది నివేదించవచ్చు. ఇది డీబగ్గింగ్ను చాలా వేగంగా మరియు సమర్థవంతంగా చేస్తుంది. - అతుకులు లేని ఇంటిగ్రేషన్: హైపోథెసిస్
pytest
మరియుunittest
వంటి ప్రముఖ టెస్టింగ్ ఫ్రేమ్వర్క్లతో సంపూర్ణంగా ఏకీకృతం అవుతుంది. మీ వర్క్ఫ్లోను మార్చకుండానే మీ ప్రస్తుత ఉదాహరణ-ఆధారిత పరీక్షలతో పాటు ప్రాపర్టీ-ఆధారిత పరీక్షలను జోడించవచ్చు. - వ్యూహాల యొక్క గొప్ప లైబ్రరీ: ఇది సాధారణ పూర్ణాంకాలు మరియు స్ట్రింగ్ల నుండి సంక్లిష్టమైన, నెస్టెడ్ డేటా స్ట్రక్చర్లు, టైమ్జోన్-అవేర్ డేట్టైమ్లు, మరియు NumPy అర్రేల వరకు ప్రతిదీ ఉత్పత్తి చేయడానికి అంతర్నిర్మిత "వ్యూహాల" యొక్క విస్తారమైన సేకరణతో వస్తుంది.
- స్టేట్ఫుల్ టెస్టింగ్: మరింత సంక్లిష్టమైన సిస్టమ్ల కోసం, స్టేట్ ట్రాన్సిషన్లలో బగ్స్ను కనుగొనడానికి హైపోథెసిస్ చర్యల శ్రేణిని పరీక్షించగలదు, ఇది ఉదాహరణ-ఆధారిత టెస్టింగ్తో చేయడం చాలా కష్టం.
ప్రారంభించడం: మీ మొదటి హైపోథెసిస్ టెస్ట్
ఇప్పుడు ఆచరణలోకి దిగుదాం. హైపోథెసిస్ను అర్థం చేసుకోవడానికి ఉత్తమ మార్గం దానిని ఆచరణలో చూడటమే.
ఇన్స్టాలేషన్
మొదట, మీరు హైపోథెసిస్ మరియు మీ ఎంపిక చేసుకున్న టెస్ట్ రన్నర్ను (మేము pytest
ఉపయోగిస్తాము) ఇన్స్టాల్ చేయాలి. ఇది చాలా సులభం:
pip install pytest hypothesis
ఒక సాధారణ ఉదాహరణ: ఒక సంపూర్ణ విలువ ఫంక్షన్ (Absolute Value Function)
ఒక సంఖ్య యొక్క సంపూర్ణ విలువను లెక్కించవలసిన ఒక సాధారణ ఫంక్షన్ను పరిగణిద్దాం. కొద్దిగా బగ్ ఉన్న ఇంప్లిమెంటేషన్ ఇలా ఉండవచ్చు:
# `my_math.py` అనే ఫైల్లో def custom_abs(x): """సంపూర్ణ విలువ ఫంక్షన్ యొక్క కస్టమ్ ఇంప్లిమెంటేషన్.""" if x < 0: return -x return x
ఇప్పుడు, ఒక టెస్ట్ ఫైల్, test_my_math.py
వ్రాద్దాం. మొదట, సాంప్రదాయ pytest
విధానం:
# test_my_math.py (ఉదాహరణ-ఆధారిత) def test_abs_positive(): assert custom_abs(5) == 5 def test_abs_negative(): assert custom_abs(-5) == 5 def test_abs_zero(): assert custom_abs(0) == 0
ఈ పరీక్షలు పాస్ అవుతాయి. ఈ ఉదాహరణల ఆధారంగా మన ఫంక్షన్ సరిగ్గా ఉన్నట్లు కనిపిస్తోంది. కానీ ఇప్పుడు, హైపోథెసిస్తో ఒక ప్రాపర్టీ-ఆధారిత పరీక్ష వ్రాద్దాం. సంపూర్ణ విలువ ఫంక్షన్ యొక్క ఒక ప్రధాన లక్షణం ఏమిటి? ఫలితం ఎప్పుడూ నెగటివ్ కాకూడదు.
# test_my_math.py (హైపోథెసిస్తో ప్రాపర్టీ-ఆధారిత) from hypothesis import given from hypothesis import strategies as st from my_math import custom_abs @given(st.integers()) def test_abs_property_is_non_negative(x): """ప్రాపర్టీ: ఏదైనా పూర్ణాంకం యొక్క సంపూర్ణ విలువ ఎల్లప్పుడూ >= 0 ఉంటుంది.""" assert custom_abs(x) >= 0
దీనిని విడమరిచి చూద్దాం:
from hypothesis import given, strategies as st
: మనం అవసరమైన భాగాలను ఇంపోర్ట్ చేసుకుంటాం.given
అనేది ఒక సాధారణ టెస్ట్ ఫంక్షన్ను ప్రాపర్టీ-ఆధారిత టెస్ట్గా మార్చే డెకరేటర్.strategies
అనేది మన డేటా జనరేటర్లను కనుగొనే మాడ్యూల్.@given(st.integers())
: ఇది టెస్ట్ యొక్క ప్రధాన భాగం.@given
డెకరేటర్ ఈ టెస్ట్ ఫంక్షన్ను చాలాసార్లు రన్ చేయమని హైపోథెసిస్కు చెబుతుంది. ప్రతి రన్లో, అది అందించిన స్ట్రాటజీ,st.integers()
ఉపయోగించి ఒక విలువను ఉత్పత్తి చేసి, దానిని మన టెస్ట్ ఫంక్షన్కు ఆర్గ్యుమెంట్x
గా పంపుతుంది.assert custom_abs(x) >= 0
: ఇది మన ప్రాపర్టీ. హైపోథెసిస్ ఏ పూర్ణాంకంx
ను ఊహించినా, మన ఫంక్షన్ యొక్క ఫలితం సున్నా కంటే ఎక్కువ లేదా సమానంగా ఉండాలని మనం నిర్ధారిస్తాము.
మీరు దీనిని pytest
తో రన్ చేసినప్పుడు, ఇది చాలా విలువలకు పాస్ అయ్యే అవకాశం ఉంది. హైపోథెసిస్ 0, -1, 1, పెద్ద పాజిటివ్ సంఖ్యలు, పెద్ద నెగటివ్ సంఖ్యలు మరియు మరిన్నింటిని ప్రయత్నిస్తుంది. మన సాధారణ ఫంక్షన్ వీటన్నింటినీ సరిగ్గా నిర్వహిస్తుంది. ఇప్పుడు, మనం ఒక బలహీనతను కనుగొనగలమో లేదో చూడటానికి వేరొక స్ట్రాటజీని ప్రయత్నిద్దాం.
# ఫ్లోటింగ్ పాయింట్ సంఖ్యలతో పరీక్షిద్దాం @given(st.floats()) def test_abs_floats_property(x): assert custom_abs(x) >= 0
మీరు దీనిని రన్ చేస్తే, హైపోథెసిస్ త్వరగా ఒక విఫలమైన కేసును కనుగొంటుంది!
Falsifying example: test_abs_floats_property(x=nan) ... assert custom_abs(nan) >= 0 AssertionError: assert nan >= 0
మన ఫంక్షన్కు float('nan')
(Not a Number) ఇచ్చినప్పుడు, అది nan
ను తిరిగి ఇస్తుందని హైపోథెసిస్ కనుగొంది. nan >= 0
అనే అసర్షన్ తప్పు. మనం బహుశా మాన్యువల్గా పరీక్షించాలని అనుకోని ఒక సూక్ష్మమైన బగ్ను కనుగొన్నాము. మనం ఈ కేసును నిర్వహించడానికి మన ఫంక్షన్ను సరిచేయవచ్చు, బహుశా ValueError
ను రైజ్ చేయడం ద్వారా లేదా ఒక నిర్దిష్ట విలువను తిరిగి ఇవ్వడం ద్వారా.
ఇంకా మంచిది ఏమిటంటే, బగ్ ఒక చాలా నిర్దిష్ట ఫ్లోట్తో ఉంటే? హైపోథెసిస్ యొక్క ష్రింకర్ ఒక పెద్ద, సంక్లిష్టమైన విఫలమైన సంఖ్యను తీసుకుని, బగ్ను ప్రేరేపించే అతి సరళమైన వెర్షన్కు తగ్గించి ఉండేది.
వ్యూహాల శక్తి: మీ టెస్ట్ డేటాను రూపొందించడం
వ్యూహాలు (Strategies) హైపోథెసిస్ యొక్క గుండెకాయ. అవి డేటాను ఉత్పత్తి చేయడానికి రెసిపీలు లాంటివి. ఈ లైబ్రరీలో అంతర్నిర్మిత వ్యూహాల యొక్క విస్తారమైన శ్రేణి ఉంది, మరియు మీరు వాటిని కలపడం మరియు అనుకూలీకరించడం ద్వారా మీరు ఊహించగల ఏ డేటా నిర్మాణాన్ని అయినా ఉత్పత్తి చేయవచ్చు.
సాధారణ అంతర్నిర్మిత వ్యూహాలు
- సంఖ్యాత్మకం:
st.integers(min_value=0, max_value=1000)
: పూర్ణాంకాలను ఉత్పత్తి చేస్తుంది, ఐచ్ఛికంగా ఒక నిర్దిష్ట పరిధిలో.st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
: ఫ్లోట్లను ఉత్పత్తి చేస్తుంది, ప్రత్యేక విలువలపై సూక్ష్మ నియంత్రణతో.st.fractions()
,st.decimals()
- టెక్స్ట్:
st.text(min_size=1, max_size=50)
: ఒక నిర్దిష్ట పొడవు గల యూనికోడ్ స్ట్రింగ్లను ఉత్పత్తి చేస్తుంది.st.text(alphabet='abcdef0123456789')
: ఒక నిర్దిష్ట అక్షర సమితి నుండి స్ట్రింగ్లను ఉత్పత్తి చేస్తుంది (ఉదా., హెక్స్ కోడ్ల కోసం).st.characters()
: వ్యక్తిగత అక్షరాలను ఉత్పత్తి చేస్తుంది.
- కలెక్షన్లు:
st.lists(st.integers(), min_size=1)
: ప్రతి ఎలిమెంట్ ఒక పూర్ణాంకంగా ఉండే జాబితాలను ఉత్పత్తి చేస్తుంది. మనం మరొక స్ట్రాటజీని ఆర్గ్యుమెంట్గా ఎలా పాస్ చేసామో గమనించండి! దీనిని కంపోజిషన్ అంటారు.st.tuples(st.text(), st.booleans())
: స్థిరమైన నిర్మాణంతో టపుల్స్ను ఉత్పత్తి చేస్తుంది.st.sets(st.integers())
st.dictionaries(keys=st.text(), values=st.integers())
: నిర్దిష్ట కీ మరియు విలువ రకాలతో డిక్షనరీలను ఉత్పత్తి చేస్తుంది.
- టెంపోరల్:
st.dates()
,st.times()
,st.datetimes()
,st.timedeltas()
. వీటిని టైమ్జోన్-అవేర్గా చేయవచ్చు.
- ఇతరాలు:
st.booleans()
:True
లేదాFalse
ఉత్పత్తి చేస్తుంది.st.just('constant_value')
: ఎల్లప్పుడూ అదే ఒకే విలువను ఉత్పత్తి చేస్తుంది. సంక్లిష్ట వ్యూహాలను కంపోజ్ చేయడానికి ఉపయోగపడుతుంది.st.one_of(st.integers(), st.text())
: అందించిన వ్యూహాలలో ఒకదాని నుండి ఒక విలువను ఉత్పత్తి చేస్తుంది.st.none()
: కేవలంNone
ను ఉత్పత్తి చేస్తుంది.
వ్యూహాలను కలపడం మరియు మార్చడం
హైపోథెసిస్ యొక్క నిజమైన శక్తి సరళమైన వ్యూహాల నుండి సంక్లిష్టమైన వాటిని నిర్మించగల సామర్థ్యంలో ఉంది.
.map()
ను ఉపయోగించడం
.map()
పద్ధతి ఒక స్ట్రాటజీ నుండి ఒక విలువను తీసుకుని దానిని మరొకదానికి మార్చడానికి మిమ్మల్ని అనుమతిస్తుంది. ఇది మీ కస్టమ్ క్లాస్ల యొక్క ఆబ్జెక్ట్లను సృష్టించడానికి సరైనది.
# ఒక సాధారణ డేటా క్లాస్ from dataclasses import dataclass @dataclass class User: user_id: int username: str # User ఆబ్జెక్ట్లను ఉత్పత్తి చేయడానికి ఒక స్ట్రాటజీ user_strategy = st.builds( User, user_id=st.integers(min_value=1), username=st.text(min_size=3, alphabet='abcdefghijklmnopqrstuvwxyz') ) @given(user=user_strategy) def test_user_creation(user): assert isinstance(user, User) assert user.user_id > 0 assert user.username.isalpha()
.filter()
మరియు assume()
ను ఉపయోగించడం
కొన్నిసార్లు మీరు ఉత్పత్తి చేసిన కొన్ని విలువలను తిరస్కరించాలి. ఉదాహరణకు, మీకు మొత్తం సున్నా కాని పూర్ణాంకాల జాబితా అవసరం కావచ్చు. మీరు .filter()
ను ఉపయోగించవచ్చు:
st.lists(st.integers()).filter(lambda x: sum(x) != 0)
అయితే, .filter()
ను ఉపయోగించడం అసమర్థంగా ఉండవచ్చు. షరతు తరచుగా తప్పు అయితే, హైపోథెసిస్ చెల్లుబాటు అయ్యే ఉదాహరణను ఉత్పత్తి చేయడానికి చాలా సమయం పట్టవచ్చు. తరచుగా ఒక మంచి విధానం మీ టెస్ట్ ఫంక్షన్ లోపల assume()
ను ఉపయోగించడం:
from hypothesis import assume @given(st.lists(st.integers())) def test_something_with_non_zero_sum_list(numbers): assume(sum(numbers) != 0) # ... ఇక్కడ మీ టెస్ట్ లాజిక్ ...
assume()
హైపోథెసిస్కు ఇలా చెబుతుంది: "ఈ షరతు నెరవేరకపోతే, ఈ ఉదాహరణను విస్మరించి, కొత్తదాన్ని ప్రయత్నించు." ఇది మీ టెస్ట్ డేటాను నిర్బంధించడానికి మరింత ప్రత్యక్ష మరియు తరచుగా మరింత సమర్థవంతమైన మార్గం.
st.composite()
ను ఉపయోగించడం
ఒక ఉత్పత్తి చేసిన విలువ మరొకదానిపై ఆధారపడి ఉండే నిజంగా సంక్లిష్టమైన డేటా జనరేషన్ కోసం, st.composite()
మీకు అవసరమైన సాధనం. ఇది ఒక ప్రత్యేక draw
ఫంక్షన్ను ఆర్గ్యుమెంట్గా తీసుకునే ఫంక్షన్ను వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది, దీనిని మీరు ఇతర వ్యూహాల నుండి దశలవారీగా విలువలను లాగడానికి ఉపయోగించవచ్చు.
ఒక క్లాసిక్ ఉదాహరణ ఒక జాబితాను మరియు ఆ జాబితాలోకి చెల్లుబాటు అయ్యే ఒక ఇండెక్స్ను ఉత్పత్తి చేయడం.
@st.composite def list_and_index(draw): # మొదట, ఒక ఖాళీ కాని జాబితాను డ్రా చేయండి my_list = draw(st.lists(st.integers(), min_size=1)) # ఆపై, ఆ జాబితాకు ఖచ్చితంగా చెల్లుబాటు అయ్యే ఒక ఇండెక్స్ను డ్రా చేయండి index = draw(st.integers(min_value=0, max_value=len(my_list) - 1)) return (my_list, index) @given(data=list_and_index()) def test_list_access(data): my_list, index = data # మనం స్ట్రాటజీని నిర్మించిన విధానం వల్ల ఈ యాక్సెస్ సురక్షితం element = my_list[index] assert element is not None # ఒక సాధారణ అసర్షన్
హైపోథెసిస్ ఆచరణలో: వాస్తవ-ప్రపంచ దృశ్యాలు
సాఫ్ట్వేర్ డెవలపర్లు ప్రతిరోజూ ఎదుర్కొనే మరింత వాస్తవిక సమస్యలకు ఈ భావనలను అన్వయిద్దాం.
దృశ్యం 1: డేటా సీరియలైజేషన్ ఫంక్షన్ను పరీక్షించడం
ఒక ఫంక్షన్ యూజర్ ప్రొఫైల్ను (ఒక డిక్షనరీ) URL-సురక్షిత స్ట్రింగ్లోకి సీరియలైజ్ చేస్తుందని మరియు మరొకటి దానిని డీసీరియలైజ్ చేస్తుందని ఊహించుకోండి. ఒక ముఖ్యమైన లక్షణం ఏమిటంటే, ఈ ప్రక్రియ సంపూర్ణంగా రివర్సిబుల్గా ఉండాలి.
import json import base64 def serialize_profile(data: dict) -> str: """ఒక డిక్షనరీని URL-సురక్షిత బేస్64 స్ట్రింగ్గా సీరియలైజ్ చేస్తుంది.""" json_string = json.dumps(data) return base64.urlsafe_b64encode(json_string.encode('utf-8')).decode('utf-8') def deserialize_profile(encoded_str: str) -> dict: """ఒక స్ట్రింగ్ను తిరిగి డిక్షనరీలోకి డీసీరియలైజ్ చేస్తుంది.""" json_string = base64.urlsafe_b64decode(encoded_str.encode('utf-8')).decode('utf-8') return json.loads(json_string) # ఇప్పుడు టెస్ట్ కోసం # మనకు JSON-అనుకూల డిక్షనరీలను ఉత్పత్తి చేసే ఒక స్ట్రాటజీ అవసరం json_dictionaries = st.dictionaries( keys=st.text(), values=st.recursive(st.none() | st.booleans() | st.floats(allow_nan=False) | st.text(), lambda children: st.lists(children) | st.dictionaries(st.text(), children), max_leaves=10) ) @given(profile=json_dictionaries) def test_serialization_roundtrip(profile): """ప్రాపర్టీ: ఎన్కోడ్ చేసిన ప్రొఫైల్ను డీసీరియలైజ్ చేయడం అసలు ప్రొఫైల్ను తిరిగి ఇవ్వాలి.""" encoded = serialize_profile(profile) decoded = deserialize_profile(encoded) assert profile == decoded
ఈ ఒకే ఒక్క టెస్ట్ మన ఫంక్షన్లను భారీ రకాల డేటాతో పరీక్షిస్తుంది: ఖాళీ డిక్షనరీలు, నెస్టెడ్ జాబితాలతో ఉన్న డిక్షనరీలు, యూనికోడ్ అక్షరాలతో ఉన్న డిక్షనరీలు, వింత కీలతో ఉన్న డిక్షనరీలు మరియు మరెన్నో. కొన్ని మాన్యువల్ ఉదాహరణలు వ్రాయడం కంటే ఇది చాలా క్షుణ్ణంగా ఉంటుంది.
దృశ్యం 2: ఒక సార్టింగ్ అల్గారిథమ్ను పరీక్షించడం
మన సార్టింగ్ ఉదాహరణను మళ్ళీ చూద్దాం. మనం ముందుగా నిర్వచించిన లక్షణాలను మీరు ఇలా పరీక్షిస్తారు.
from collections import Counter def my_buggy_sort(numbers): # ఒక సూక్ష్మమైన బగ్ను పరిచయం చేద్దాం: ఇది డూప్లికేట్లను తొలగిస్తుంది return sorted(list(set(numbers))) @given(st.lists(st.integers())) def test_sorting_properties(numbers): sorted_list = my_buggy_sort(numbers) # ప్రాపర్టీ 1: అవుట్పుట్ క్రమబద్ధంగా ఉంటుంది for i in range(len(sorted_list) - 1): assert sorted_list[i] <= sorted_list[i+1] # ప్రాపర్టీ 2: మూలకాలు అవే (ఇది బగ్ను కనుగొంటుంది) assert Counter(numbers) == Counter(sorted_list) # ప్రాపర్టీ 3: ఫంక్షన్ ఐడెంపోటెంట్ assert my_buggy_sort(sorted_list) == sorted_list
మీరు ఈ టెస్ట్ను రన్ చేసినప్పుడు, హైపోథెసిస్ ప్రాపర్టీ 2 కోసం numbers=[0, 0]
వంటి విఫలమైన ఉదాహరణను త్వరగా కనుగొంటుంది. మన ఫంక్షన్ [0]
ను తిరిగి ఇస్తుంది, మరియు Counter([0, 0])
, Counter([0])
కు సమానం కాదు. ష్రింకర్ విఫలమైన ఉదాహరణ వీలైనంత సరళంగా ఉండేలా చూస్తుంది, దీనివల్ల బగ్ యొక్క కారణం వెంటనే స్పష్టమవుతుంది.
దృశ్యం 3: స్టేట్ఫుల్ టెస్టింగ్
కాలక్రమేణా అంతర్గత స్థితి మారే ఆబ్జెక్ట్ల కోసం (డేటాబేస్ కనెక్షన్, షాపింగ్ కార్ట్, లేదా కాష్ వంటివి), బగ్స్ను కనుగొనడం చాలా కష్టంగా ఉంటుంది. ఒక లోపాన్ని ప్రేరేపించడానికి ఒక నిర్దిష్ట శ్రేణి కార్యకలాపాలు అవసరం కావచ్చు. హైపోథెసిస్ ఈ ప్రయోజనం కోసం RuleBasedStateMachine
ను అందిస్తుంది.
ఒక ఇన్-మెమరీ కీ-విలువ స్టోర్ కోసం ఒక సాధారణ API ని ఊహించుకోండి:
class SimpleKeyValueStore: def __init__(self): self._data = {} def set(self, key, value): self._data[key] = value def get(self, key): return self._data.get(key) def delete(self, key): if key in self._data: del self._data[key] def size(self): return len(self._data)
మనం దాని ప్రవర్తనను మోడల్ చేసి, స్టేట్ మెషీన్తో పరీక్షించవచ్చు:
from hypothesis.stateful import RuleBasedStateMachine, rule, Bundle class KeyValueStoreMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.model = {} self.sut = SimpleKeyValueStore() # Bundle() నియమాల మధ్య డేటాను పంపడానికి ఉపయోగించబడుతుంది keys = Bundle('keys') @rule(target=keys, key=st.text(), value=st.integers()) def set_key(self, key, value): self.model[key] = value self.sut.set(key, value) return key @rule(key=keys) def delete_key(self, key): del self.model[key] self.sut.delete(key) @rule(key=st.text()) def get_key(self, key): model_val = self.model.get(key) sut_val = self.sut.get(key) assert model_val == sut_val @rule() def check_size(self): assert len(self.model) == self.sut.size() # టెస్ట్ను రన్ చేయడానికి, మీరు కేవలం మెషీన్ మరియు unittest.TestCase నుండి సబ్క్లాస్ చేయాలి # pytest లో, మీరు కేవలం టెస్ట్ను మెషీన్ క్లాస్కు కేటాయించవచ్చు TestKeyValueStore = KeyValueStoreMachine.TestCase
హైపోథెసిస్ ఇప్పుడు `set_key`, `delete_key`, `get_key`, మరియు `check_size` ఆపరేషన్ల యాదృచ్ఛిక శ్రేణులను అమలు చేస్తుంది, అసర్షన్లలో ఒకదానిని విఫలం చేసే శ్రేణిని కనుగొనడానికి నిరంతరం ప్రయత్నిస్తుంది. ఇది తొలగించిన కీని పొందడం సరిగ్గా ప్రవర్తిస్తుందో లేదో, బహుళ సెట్లు మరియు డిలీట్ల తర్వాత సైజ్ స్థిరంగా ఉందో లేదో, మరియు మీరు మాన్యువల్గా పరీక్షించాలనే ఆలోచన రాని అనేక ఇతర దృశ్యాలను తనిఖీ చేస్తుంది.
ఉత్తమ పద్ధతులు మరియు అధునాతన చిట్కాలు
- ఉదాహరణ డేటాబేస్: హైపోథెసిస్ తెలివైనది. అది ఒక బగ్ను కనుగొన్నప్పుడు, అది విఫలమైన ఉదాహరణను ఒక స్థానిక డైరెక్టరీలో (
.hypothesis/
) సేవ్ చేస్తుంది. మీరు మీ పరీక్షలను తదుపరిసారి రన్ చేసినప్పుడు, అది మొదట ఆ విఫలమైన ఉదాహరణను రీప్లే చేస్తుంది, బగ్ ఇంకా ఉందని మీకు తక్షణ ఫీడ్బ్యాక్ ఇస్తుంది. మీరు దానిని సరిచేసిన తర్వాత, ఆ ఉదాహరణ ఇకపై రీప్లే చేయబడదు. @settings
తో టెస్ట్ ఎగ్జిక్యూషన్ను నియంత్రించడం: మీరు@settings
డెకరేటర్ను ఉపయోగించి టెస్ట్ రన్ యొక్క అనేక అంశాలను నియంత్రించవచ్చు. మీరు ఉదాహరణల సంఖ్యను పెంచవచ్చు, ఒకే ఉదాహరణ ఎంత సేపు రన్ కావచ్చో ఒక గడువును సెట్ చేయవచ్చు (అనంతమైన లూప్లను పట్టుకోవడానికి), మరియు కొన్ని హెల్త్ చెక్లను ఆఫ్ చేయవచ్చు.@settings(max_examples=500, deadline=1000) # 500 ఉదాహరణలు రన్ చేయండి, 1-సెకండ్ గడువు @given(...) ...
- వైఫల్యాలను పునరుత్పత్తి చేయడం: ప్రతి హైపోథెసిస్ రన్ ఒక సీడ్ విలువను ప్రింట్ చేస్తుంది (ఉదా.,
@reproduce_failure('version', 'seed')
). ఒక CI సర్వర్ మీరు స్థానికంగా పునరుత్పత్తి చేయలేని బగ్ను కనుగొంటే, మీరు అందించిన సీడ్తో ఈ డెకరేటర్ను ఉపయోగించి అదే ఉదాహరణల శ్రేణిని హైపోథెసిస్ను బలవంతంగా రన్ చేయించవచ్చు. - CI/CD తో ఇంటిగ్రేషన్: హైపోథెసిస్ ఏ కంటిన్యూస్ ఇంటిగ్రేషన్ పైప్లైన్కైనా సరైనది. ఉత్పత్తికి చేరకముందే అస్పష్టమైన బగ్లను కనుగొనగల దాని సామర్థ్యం దీనిని ఒక అమూల్యమైన భద్రతా వలయంగా చేస్తుంది.
ఆలోచనా విధానంలో మార్పు: ప్రాపర్టీలలో ఆలోచించడం
హైపోథెసిస్ను స్వీకరించడం అనేది కేవలం ఒక కొత్త లైబ్రరీని నేర్చుకోవడం కంటే ఎక్కువ; ఇది మీ కోడ్ యొక్క ఖచ్చితత్వం గురించి కొత్తగా ఆలోచించే విధానాన్ని స్వీకరించడం. "నేను ఏ ఇన్పుట్లను పరీక్షించాలి?" అని అడగడానికి బదులుగా, మీరు "ఈ కోడ్ గురించి విశ్వవ్యాప్త సత్యాలు ఏమిటి?" అని అడగడం ప్రారంభిస్తారు.
ప్రాపర్టీలను గుర్తించడానికి ప్రయత్నిస్తున్నప్పుడు మీకు మార్గనిర్దేశం చేసే కొన్ని ప్రశ్నలు ఇక్కడ ఉన్నాయి:
- ఒక రివర్స్ ఆపరేషన్ ఉందా? (ఉదా., సీరియలైజ్/డీసీరియలైజ్, ఎన్క్రిప్ట్/డీక్రిప్ట్, కంప్రెస్/డీకంప్రెస్). ఆపరేషన్ మరియు దాని రివర్స్ను నిర్వహించడం అసలు ఇన్పుట్ను తిరిగి ఇవ్వాలనేది ప్రాపర్టీ.
- ఆపరేషన్ ఐడెంపోటెంట్ కాదా? (ఉదా.,
abs(abs(x)) == abs(x)
). ఫంక్షన్ను ఒకటి కంటే ఎక్కువసార్లు వర్తింపజేయడం ఒకసారి వర్తింపజేసినట్లే అదే ఫలితాన్ని ఇవ్వాలి. - అదే ఫలితాన్ని లెక్కించడానికి వేరే, సరళమైన మార్గం ఉందా? మీ సంక్లిష్ట, ఆప్టిమైజ్ చేసిన ఫంక్షన్ ఒక సరళమైన, స్పష్టంగా సరైన వెర్షన్ వలె అదే అవుట్పుట్ను ఉత్పత్తి చేస్తుందో లేదో మీరు పరీక్షించవచ్చు (ఉదా., మీ ఫ్యాన్సీ సార్ట్ను పైథాన్ యొక్క అంతర్నిర్మిత
sorted()
తో పరీక్షించడం). - అవుట్పుట్ గురించి ఎల్లప్పుడూ నిజం ఏమిటి? (ఉదా., ఒక `find_prime_factors` ఫంక్షన్ యొక్క అవుట్పుట్ కేవలం ప్రధాన సంఖ్యలను మాత్రమే కలిగి ఉండాలి, మరియు వాటి గుణకారం ఇన్పుట్కు సమానంగా ఉండాలి).
- స్థితి ఎలా మారుతుంది? (స్టేట్ఫుల్ టెస్టింగ్ కోసం) ఏవైనా చెల్లుబాటు అయ్యే ఆపరేషన్ తర్వాత ఏ అంతర్లీన నియమాలు పాటించాలి? (ఉదా., షాపింగ్ కార్ట్లోని వస్తువుల సంఖ్య ఎప్పుడూ నెగటివ్ కాకూడదు).
ముగింపు: ఒక కొత్త స్థాయి ఆత్మవిశ్వాసం
హైపోథెసిస్తో ప్రాపర్టీ-బేస్డ్ టెస్టింగ్ ఉదాహరణ-ఆధారిత టెస్టింగ్ను భర్తీ చేయదు. క్లిష్టమైన వ్యాపార తర్కం మరియు బాగా అర్థం చేసుకున్న అవసరాల కోసం మీకు ఇప్పటికీ నిర్దిష్ట, చేతితో వ్రాసిన పరీక్షలు అవసరం (ఉదా., "దేశం X నుండి ఒక వినియోగదారుడు ధర Y ను చూడాలి").
హైపోథెసిస్ అందించేది మీ కోడ్ యొక్క ప్రవర్తనను అన్వేషించడానికి మరియు ఊహించని ఎడ్జ్ కేసులకు వ్యతిరేకంగా రక్షణ కల్పించడానికి ఒక శక్తివంతమైన, ఆటోమేటెడ్ మార్గం. ఇది వేలాది పరీక్షలను ఉత్పత్తి చేస్తూ, ఏ మనిషి వాస్తవికంగా వ్రాయగల దానికంటే వైవిధ్యంగా మరియు మోసపూరితంగా ఉండే ఒక అలసిపోని భాగస్వామిగా పనిచేస్తుంది. మీ కోడ్ యొక్క ప్రాథమిక లక్షణాలను నిర్వచించడం ద్వారా, మీరు హైపోథెసిస్ పరీక్షించగల ఒక పటిష్టమైన స్పెసిఫికేషన్ను సృష్టిస్తారు, ఇది మీ సాఫ్ట్వేర్లో మీకు ఒక కొత్త స్థాయి ఆత్మవిశ్వాసాన్ని ఇస్తుంది.
తదుపరిసారి మీరు ఒక ఫంక్షన్ వ్రాసినప్పుడు, ఉదాహరణలకు మించి ఆలోచించడానికి ఒక క్షణం తీసుకోండి. మిమ్మల్ని మీరు ప్రశ్నించుకోండి, "నియమాలు ఏమిటి? ఎల్లప్పుడూ నిజం ఏమిటి?" ఆపై, వాటిని ఉల్లంఘించడానికి ప్రయత్నించే కష్టమైన పనిని హైపోథెసిస్కు వదిలేయండి. అది కనుగొన్నదానికి మీరు ఆశ్చర్యపోతారు, మరియు మీ కోడ్ దానివల్ల మెరుగుపడుతుంది.