പൈത്തണിന്റെ ഹൈപ്പോതിസിസ് ലൈബ്രറി ഉപയോഗിച്ച് പ്രോപ്പർട്ടി-ബേസ്ഡ് ടെസ്റ്റിംഗ് കണ്ടെത്തുക. ഉദാഹരണങ്ങളെ അടിസ്ഥാനമാക്കിയുള്ള ടെസ്റ്റുകൾക്കപ്പുറം അരികിലുള്ള കേസുകൾ കണ്ടെത്തി കൂടുതൽ ശക്തവും വിശ്വസനീയവുമായ സോഫ്റ്റ്വെയർ നിർമ്മിക്കുക.
യൂണിറ്റ് ടെസ്റ്റുകൾക്കപ്പുറം: പൈത്തണിന്റെ ഹൈപ്പോതിസിസ് ഉപയോഗിച്ചുള്ള പ്രോപ്പർട്ടി-ബേസ്ഡ് ടെസ്റ്റിംഗിലേക്കുള്ള ഒരു ആഴത്തിലുള്ള പഠനം
സോഫ്റ്റ്വെയർ വികസന ലോകത്ത്, ടെസ്റ്റിംഗ് ഗുണമേന്മയുടെ അടിസ്ഥാനമാണ്. പതിറ്റാണ്ടുകളായി, ഉദാഹരണങ്ങളെ അടിസ്ഥാനമാക്കിയുള്ള ടെസ്റ്റിംഗ് ആയിരുന്നു പ്രബലമായ മാതൃക. നമ്മൾ ഇൻപുട്ടുകൾ ശ്രദ്ധാപൂർവ്വം തയ്യാറാക്കുന്നു, പ്രതീക്ഷിക്കുന്ന ഔട്ട്പുട്ടുകൾ നിർവചിക്കുന്നു, നമ്മുടെ കോഡ് ആസൂത്രണം ചെയ്തതുപോലെ പ്രവർത്തിക്കുന്നുണ്ടോയെന്ന് പരിശോധിക്കാൻ അസേർഷനുകൾ എഴുതുന്നു. 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)
.
ഈ സമീപനത്തിലൂടെ, നിങ്ങൾ ടെസ്റ്റ് ഡാറ്റ എഴുതുന്നില്ല. നിങ്ങൾ നിയമങ്ങൾ എഴുതുന്നു. തുടർന്ന്, ഹൈപ്പോതിസിസ് പോലുള്ള ഒരു ഫ്രെയിംവർക്കിനെ നിങ്ങളുടെ പ്രോപ്പർട്ടികൾ തെളിയിക്കാൻ നൂറുകണക്കിന് അല്ലെങ്കിൽ ആയിരക്കണക്കിന് ക്രമരഹിതവും വൈവിധ്യമാർന്നതും പലപ്പോഴും തന്ത്രപരവുമായ ഇൻപുട്ടുകൾ സൃഷ്ടിക്കാൻ നിങ്ങൾ അനുവദിക്കുന്നു. ഒരു പ്രോപ്പർട്ടി ലംഘിക്കുന്ന ഒരു ഇൻപുട്ട് അത് കണ്ടെത്തുകയാണെങ്കിൽ, ഒരു ബഗ് കണ്ടെത്തിയിരിക്കുന്നു.
ഹൈപ്പോതിസിസ് അവതരിപ്പിക്കുന്നു: നിങ്ങളുടെ ഓട്ടോമേറ്റഡ് ടെസ്റ്റ് ഡാറ്റ ജനറേറ്റർ
പൈത്തണിനായുള്ള പ്രധാന പ്രോപ്പർട്ടി-ബേസ്ഡ് ടെസ്റ്റിംഗ് ലൈബ്രറിയാണ് ഹൈപ്പോതിസിസ്. നിങ്ങൾ നിർവചിക്കുന്ന പ്രോപ്പർട്ടികൾ ഇത് സ്വീകരിക്കുകയും അവയെ ചോദ്യം ചെയ്യാൻ ടെസ്റ്റ് ഡാറ്റ ഉത്പാദിപ്പിക്കുന്നതിനുള്ള കഠിനാധ്വാനം ചെയ്യുകയും ചെയ്യുന്നു. ഇത് ഒരു ക്രമരഹിതമായ ഡാറ്റാ ജനറേറ്റർ മാത്രമല്ല; കാര്യക്ഷമമായി ബഗുകൾ കണ്ടെത്താൻ രൂപകൽപ്പന ചെയ്ത ഒരു ബുദ്ധിപരവും ശക്തവുമായ ഉപകരണമാണിത്.
ഹൈപ്പോതിസിസിന്റെ പ്രധാന സവിശേഷതകൾ
- ഓട്ടോമാറ്റിക് ടെസ്റ്റ് കേസ് ജനറേഷൻ: നിങ്ങൾക്ക് ആവശ്യമുള്ള ഡാറ്റയുടെ *രൂപം* നിങ്ങൾ നിർവചിക്കുന്നു (ഉദാഹരണത്തിന്, "പൂർണ്ണസംഖ്യകളുടെ ഒരു ലിസ്റ്റ്," "അക്ഷരങ്ങൾ മാത്രമടങ്ങിയ ഒരു സ്ട്രിംഗ്," "ഭാവിയിലെ ഒരു ഡേറ്റ് ടൈം"), ഹൈപ്പോതിസിസ് ആ രൂപത്തിന് അനുയോജ്യമായ വിവിധതരം ഉദാഹരണങ്ങൾ സൃഷ്ടിക്കുന്നു.
- ഇന്റലിജന്റ് ഷ്രിങ്കിംഗ്: ഇതാണ് മാന്ത്രിക സവിശേഷത. ഹൈപ്പോതിസിസ് ഒരു പരാജയപ്പെടുന്ന ടെസ്റ്റ് കേസ് കണ്ടെത്തുമ്പോൾ (ഉദാഹരണത്തിന്, നിങ്ങളുടെ സോർട്ട് ഫംഗ്ഷനെ തകരാറിലാക്കുന്ന 50 കോംപ്ലക്സ് സംഖ്യകളുടെ ഒരു ലിസ്റ്റ്), ആ വലിയ ലിസ്റ്റ് മാത്രം റിപ്പോർട്ട് ചെയ്യുന്നില്ല. പരാജയത്തിന് കാരണമാകുന്ന ഏറ്റവും ചെറിയ ഉദാഹരണം കണ്ടെത്താൻ ഇത് ഇൻപുട്ടിനെ ബുദ്ധിപരമായും സ്വയമേവയും ലളിതമാക്കുന്നു. 50-ഘടക ലിസ്റ്റിന് പകരം,
[inf, nan]
ഉപയോഗിച്ച് പരാജയം സംഭവിക്കുന്നു എന്ന് ഇത് റിപ്പോർട്ട് ചെയ്തേക്കാം. ഇത് ഡീബഗ്ഗിംഗ് അതിവേഗവും കാര്യക്ഷമവുമാക്കുന്നു. - തടസ്സമില്ലാത്ത സംയോജനം:
pytest
,unittest
പോലുള്ള ജനപ്രിയ ടെസ്റ്റിംഗ് ഫ്രെയിംവർക്കുകളുമായി ഹൈപ്പോതിസിസ് തികച്ചും സംയോജിക്കുന്നു. നിങ്ങളുടെ വർക്ക്ഫ്ലോയിൽ മാറ്റം വരുത്താതെ നിലവിലുള്ള ഉദാഹരണങ്ങളെ അടിസ്ഥാനമാക്കിയുള്ള ടെസ്റ്റുകൾക്കൊപ്പം പ്രോപ്പർട്ടി-ബേസ്ഡ് ടെസ്റ്റുകൾ ചേർക്കാൻ നിങ്ങൾക്ക് കഴിയും. - തന്ത്രങ്ങളുടെ സമ്പന്നമായ ലൈബ്രറി: ലളിതമായ പൂർണ്ണസംഖ്യകളും സ്ട്രിംഗുകളും മുതൽ സങ്കീർണ്ണവും നെസ്റ്റഡ് ആയതുമായ ഡാറ്റാ ഘടനകൾ, ടൈംസോൺ-അവെയർ ഡേറ്റ് ടൈമുകൾ, കൂടാതെ നംപൈ അറേകൾ വരെ ഉത്പാദിപ്പിക്കുന്നതിനുള്ള വിപുലമായ അന്തർനിർമ്മിത "തന്ത്രങ്ങളുടെ" ശേഖരം ഇതിൽ ഉൾപ്പെടുന്നു.
- സ്റ്റേറ്റ്ഫുൾ ടെസ്റ്റിംഗ്: കൂടുതൽ സങ്കീർണ്ണമായ സിസ്റ്റങ്ങൾക്ക്, സ്റ്റേറ്റ് ട്രാൻസിഷനുകളിലെ ബഗുകൾ കണ്ടെത്താൻ ഹൈപ്പോതിസിസിന് പ്രവർത്തനങ്ങളുടെ ശ്രേണികൾ പരിശോധിക്കാൻ കഴിയും, ഇത് ഉദാഹരണങ്ങളെ അടിസ്ഥാനമാക്കിയുള്ള ടെസ്റ്റിംഗ് വഴി കണ്ടെത്താൻ വളരെ പ്രയാസമാണ്.
തുടക്കം: നിങ്ങളുടെ ആദ്യത്തെ ഹൈപ്പോതിസിസ് ടെസ്റ്റ്
നമുക്ക് നേരിട്ട് പരീക്ഷിച്ചു നോക്കാം. ഹൈപ്പോതിസിസ് മനസ്സിലാക്കാനുള്ള ഏറ്റവും നല്ല മാർഗ്ഗം അത് പ്രവർത്തിക്കുന്നത് കാണുക എന്നതാണ്.
ഇൻസ്റ്റാളേഷൻ
ആദ്യം, ഹൈപ്പോതിസിസും നിങ്ങൾക്ക് ഇഷ്ടമുള്ള ടെസ്റ്റ് റണ്ണറും (ഞങ്ങൾ pytest
ഉപയോഗിക്കും) ഇൻസ്റ്റാൾ ചെയ്യേണ്ടതുണ്ട്. ഇത് വളരെ ലളിതമാണ്:
pip install pytest hypothesis
ഒരു ലളിതമായ ഉദാഹരണം: ഒരു അബ്സല്യൂട്ട് വാല്യൂ ഫംഗ്ഷൻ
ഒരു സംഖ്യയുടെ അബ്സല്യൂട്ട് മൂല്യം കണക്കാക്കാൻ ഉദ്ദേശിച്ചുള്ള ഒരു ലളിതമായ ഫംഗ്ഷൻ നമുക്ക് പരിഗണിക്കാം. അല്പം ബഗുകളുള്ള ഒരു നിർവ്വഹണം ഇങ്ങനെയുണ്ടാകാം:
# `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
നിങ്ങൾ ഇത് പ്രവർത്തിപ്പിക്കുകയാണെങ്കിൽ, ഹൈപ്പോതിസിസ് വേഗത്തിൽ ഒരു പരാജയപ്പെടുന്ന കേസ് കണ്ടെത്തും!
തെളിയിക്കുന്ന ഉദാഹരണം: test_abs_floats_property(x=nan) ... assert custom_abs(nan) >= 0 AssertionError: assert nan >= 0
നമ്മുടെ ഫംഗ്ഷന് float('nan')
(സംഖ്യയല്ല) നൽകുമ്പോൾ, അത് nan
തിരികെ നൽകുന്നു എന്ന് ഹൈപ്പോതിസിസ് കണ്ടെത്തി. nan >= 0
എന്ന അസേർഷൻ തെറ്റാണ്. നമ്മൾ കൈകൊണ്ട് പരീക്ഷിക്കാൻ സാധ്യതയില്ലാത്ത ഒരു സൂക്ഷ്മമായ ബഗ് ഇപ്പോൾ കണ്ടെത്തി. ഒരുപക്ഷേ ഒരു ValueError
ഉയർത്തിക്കൊണ്ടോ അല്ലെങ്കിൽ ഒരു പ്രത്യേക മൂല്യം തിരികെ നൽകിക്കൊണ്ടോ ഈ കേസ് കൈകാര്യം ചെയ്യാൻ നമുക്ക് നമ്മുടെ ഫംഗ്ഷൻ പരിഹരിക്കാൻ കഴിയും.
അതിലും മികച്ചത്, ഒരു പ്രത്യേക ഫ്ലോട്ട് മൂലകത്തിലായിരുന്നു ബഗ് എങ്കിലോ? ഹൈപ്പോതിസിസിന്റെ ഷ്രിങ്കർ ഒരു വലിയ, സങ്കീർണ്ണമായ പരാജയപ്പെടുന്ന സംഖ്യയെ എടുത്ത്, ബഗ് ഇപ്പോഴും ട്രിഗർ ചെയ്യുന്ന ഏറ്റവും ലളിതമായ രൂപത്തിലേക്ക് ചുരുക്കുമായിരുന്നു.
തന്ത്രങ്ങളുടെ ശക്തി: നിങ്ങളുടെ ടെസ്റ്റ് ഡാറ്റ തയ്യാറാക്കുന്നു
തന്ത്രങ്ങളാണ് ഹൈപ്പോതിസിസിന്റെ ഹൃദയം. അവ ഡാറ്റ ഉത്പാദിപ്പിക്കുന്നതിനുള്ള പാചകക്കുറിപ്പുകളാണ്. ലൈബ്രറിയിൽ അന്തർനിർമ്മിത തന്ത്രങ്ങളുടെ ഒരു വലിയ നിരയുണ്ട്, നിങ്ങൾക്ക് സങ്കൽപ്പിക്കാൻ കഴിയുന്ന ഏത് ഡാറ്റാ ഘടനയും ഉത്പാദിപ്പിക്കുന്നതിന് അവ സംയോജിപ്പിക്കാനും ഇഷ്ടാനുസൃതമാക്കാനും കഴിയും.
പൊതുവായ അന്തർനിർമ്മിത തന്ത്രങ്ങൾ
- സംഖ്യകൾ:
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-സുരക്ഷിതമായ base64 സ്ട്രിംഗിലേക്ക് സീരിയലൈസ് ചെയ്യുന്നു.""" 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 വില കാണണം") നിങ്ങൾക്ക് ഇപ്പോഴും നിർദ്ദിഷ്ടവും കൈകൊണ്ട് എഴുതിയതുമായ ടെസ്റ്റുകൾ ആവശ്യമാണ്.
നിങ്ങളുടെ കോഡിന്റെ സ്വഭാവം പര്യവേക്ഷണം ചെയ്യാനും മുൻകൂട്ടി കാണാത്ത അരികിലുള്ള കേസുകൾക്കെതിരെ സംരക്ഷണം നൽകാനുമുള്ള ശക്തമായ, ഓട്ടോമേറ്റഡ് മാർഗ്ഗമാണ് ഹൈപ്പോതിസിസ് നൽകുന്നത്. യാതൊരു മനുഷ്യനും യഥാർത്ഥത്തിൽ എഴുതാൻ കഴിയുന്നതിനേക്കാൾ വൈവിധ്യമാർന്നതും തന്ത്രപരവുമായ ആയിരക്കണക്കിന് ടെസ്റ്റുകൾ ഇത് ഉത്പാദിപ്പിക്കുന്നു. നിങ്ങളുടെ കോഡിന്റെ അടിസ്ഥാന പ്രോപ്പർട്ടികൾ നിർവചിക്കുന്നതിലൂടെ, ഹൈപ്പോതിസിസിന് പരിശോധിക്കാൻ കഴിയുന്ന ഒരു ശക്തമായ സ്പെസിഫിക്കേഷൻ നിങ്ങൾ സൃഷ്ടിക്കുന്നു, ഇത് നിങ്ങളുടെ സോഫ്റ്റ്വെയറിൽ പുതിയൊരു ആത്മവിശ്വാസം നൽകുന്നു.
അടുത്ത തവണ നിങ്ങൾ ഒരു ഫംഗ്ഷൻ എഴുതുമ്പോൾ, ഉദാഹരണങ്ങൾക്കപ്പുറം ചിന്തിക്കാൻ ഒരു നിമിഷം എടുക്കുക. സ്വയം ചോദിക്കുക, "എന്താണ് നിയമങ്ങൾ? എപ്പോഴും എന്താണ് ശരിയായിരിക്കേണ്ടത്?" തുടർന്ന്, അവ ലംഘിക്കാൻ ശ്രമിക്കുന്നതിന്റെ കഠിനാധ്വാനം ഹൈപ്പോതിസിസിനെക്കൊണ്ട് ചെയ്യിക്കുക. അത് കണ്ടെത്തുന്നത് എന്താണെന്ന് നിങ്ങൾ ആശ്ചര്യപ്പെടും, നിങ്ങളുടെ കോഡ് അതിലൂടെ മെച്ചപ്പെടുകയും ചെയ്യും.