Tutustu ominaisuuspohjaiseen testaukseen Pythonin Hypothesis-kirjastolla. Löydä reunatapaukset ja rakenna vankempaa ja luotettavampaa ohjelmistoa esimerkkipohjaisten testien sijaan.
Yksikkötestien tuolla puolen: Syväsukellus ominaisuuspohjaiseen testaukseen Pythonin Hypothesis-kirjastolla
Ohjelmistokehityksen maailmassa testaus on laadun peruskivi. Vuosikymmenten ajan hallitseva paradigma on ollut esimerkkipohjainen testaus. Laadimme huolellisesti syötteitä, määrittelemme odotetut tulosteet ja kirjoitamme väittämiä varmistaaksemme, että koodimme toimii suunnitellusti. Tämä lähestymistapa, jota käytetään esimerkiksi unittest
- ja pytest
-kehyksissä, on tehokas ja välttämätön. Mutta entä jos kertoisin, että on olemassa täydentävä lähestymistapa, joka voi paljastaa bugeja, joita et edes osannut etsiä?
Tervetuloa ominaisuuspohjaisen testauksen maailmaan. Tämä paradigma siirtää painopisteen yksittäisten esimerkkien testaamisesta koodin yleisten ominaisuuksien todentamiseen. Ja Python-ekosysteemissä tämän lähestymistavan kiistaton mestari on kirjasto nimeltä Hypothesis.
Tämä kattava opas vie sinut täydellisestä aloittelijasta varmaksi ominaisuuspohjaisen testauksen harjoittajaksi Hypothesiksen avulla. Tutkimme ydinkäsitteitä, syvennymme käytännön esimerkkeihin ja opimme integroimaan tämän tehokkaan työkalun päivittäiseen kehitystyönkulkuusi rakentaaksemme vankempia, luotettavampia ja buginkestävämpiä ohjelmistoja.
Mitä on ominaisuuspohjainen testaus? Ajattelutavan muutos
Ymmärtääksemme Hypothesista meidän on ensin ymmärrettävä ominaisuuspohjaisen testauksen perusidea. Verrataan sitä perinteiseen esimerkkipohjaiseen testaukseen, jonka me kaikki tunnemme.
Esimerkkipohjainen testaus: Tuttu polku
Kuvittele, että olet kirjoittanut oman lajittelufunktion, my_sort()
. Esimerkkipohjaisessa testauksessa ajatusprosessisi olisi:
- "Testataanpa sitä yksinkertaisella, järjestetyllä listalla." ->
assert my_sort([1, 2, 3]) == [1, 2, 3]
- "Entä käänteisessä järjestyksessä oleva lista?" ->
assert my_sort([3, 2, 1]) == [1, 2, 3]
- "Miten olisi tyhjä lista?" ->
assert my_sort([]) == []
- "Lista, jossa on kaksoiskappaleita?" ->
assert my_sort([5, 1, 5, 2]) == [1, 2, 5, 5]
- "Ja lista, jossa on negatiivisia lukuja?" ->
assert my_sort([-1, -5, 0]) == [-5, -1, 0]
Tämä on tehokasta, mutta sillä on perustavanlaatuinen rajoitus: testaat vain tapauksia, joita osaat ajatella. Testisi ovat vain niin hyviä kuin mielikuvituksesi. Saatat jättää huomiotta reunatapauksia, jotka liittyvät erittäin suuriin lukuihin, liukulukujen epätarkkuuksiin, tiettyihin unicode-merkkeihin tai monimutkaisiin datayhdistelmiin, jotka johtavat odottamattomaan käyttäytymiseen.
Ominaisuuspohjainen testaus: Invariantteina ajattelu
Ominaisuuspohjainen testaus kääntää käsikirjoituksen. Sen sijaan, että antaisit tiettyjä esimerkkejä, määrittelet funktion ominaisuudet eli invariantit – säännöt, joiden tulisi päteä mille tahansa kelvolliselle syötteelle. Meidän my_sort()
-funktiollemme nämä ominaisuudet voisivat olla:
- Tuloste on järjestetty: Mille tahansa lukulistalle jokainen elementti tulostelistassa on pienempi tai yhtä suuri kuin sitä seuraava.
- Tuloste sisältää samat elementit kuin syöte: Järjestetty lista on vain alkuperäisen listan permutaatio; elementtejä ei lisätä eikä menetetä.
- Funktio on idempotenttinen: Jo järjestetyn listan lajittelun ei pitäisi muuttaa sitä. Toisin sanoen,
my_sort(my_sort(jokin_lista)) == my_sort(jokin_lista)
.
Tällä lähestymistavalla et kirjoita testidataa. Kirjoitat sääntöjä. Sitten annat Hypothesiksen kaltaisen kehyksen generoida satoja tai tuhansia satunnaisia, monipuolisia ja usein ovelia syötteitä yrittääkseen todistaa ominaisuutesi vääriksi. Jos se löytää syötteen, joka rikkoo ominaisuuden, se on löytänyt bugin.
Esittelyssä Hypothesis: Automaattinen testidatan generaattorisi
Hypothesis on Pythonin johtava ominaisuuspohjaisen testauksen kirjasto. Se ottaa määrittelemäsi ominaisuudet ja tekee kovan työn generoidessaan testidataa haastaakseen ne. Se ei ole vain satunnainen datageneraattori; se on älykäs ja tehokas työkalu, joka on suunniteltu löytämään bugeja tehokkaasti.
Hypothesiksen avainominaisuudet
- Automaattinen testitapausten generointi: Määrittelet tarvitsemasi datan *muodon* (esim. "lista kokonaislukuja", "merkkijono, joka sisältää vain kirjaimia", "tulevaisuudessa oleva päivämäärä ja aika"), ja Hypothesis generoi laajan valikoiman esimerkkejä, jotka vastaavat tätä muotoa.
- Älykäs pienentäminen (Shrinking): Tämä on taianomainen ominaisuus. Kun Hypothesis löytää virheellisen testitapauksen (esim. 50 kompleksiluvun lista, joka kaataa lajittelufunktiosi), se ei vain raportoi tuota massiivista listaa. Se yksinkertaistaa älykkäästi ja automaattisesti syötettä löytääkseen pienimmän mahdollisen esimerkin, joka edelleen aiheuttaa virheen. 50 elementin listan sijaan se saattaa raportoida, että virhe tapahtuu pelkällä
[inf, nan]
-syötteellä. Tämä tekee virheenkorjauksesta uskomattoman nopeaa ja tehokasta. - Saumaton integraatio: Hypothesis integroituu täydellisesti suosittuihin testauskehyksiin, kuten
pytest
jaunittest
. Voit lisätä ominaisuuspohjaisia testejä olemassa olevien esimerkkipohjaisten testien rinnalle muuttamatta työnkulkuasi. - Rikas strategiikirjasto: Siinä on laaja kokoelma sisäänrakennettuja "strategioita" kaiken generoimiseen yksinkertaisista kokonaisluvuista ja merkkijonoista monimutkaisiin, sisäkkäisiin tietorakenteisiin, aikavyöhyketietoisiin päivämääriin ja aikoihin ja jopa NumPy-taulukoihin.
- Tilallinen testaus (Stateful Testing): Monimutkaisempia järjestelmiä varten Hypothesis voi testata toimintosarjoja löytääkseen bugeja tilasiirtymistä, mikä on tunnetusti vaikeaa esimerkkipohjaisella testauksella.
Aloittaminen: Ensimmäinen Hypothesis-testisi
Kääritään hihat. Paras tapa ymmärtää Hypothesista on nähdä se toiminnassa.
Asennus
Ensin sinun on asennettava Hypothesis ja valitsemasi testiajuri (käytämme pytest
iä). Se on niinkin yksinkertaista kuin:
pip install pytest hypothesis
Yksinkertainen esimerkki: Itseisarvofunktio
Tarkastellaan yksinkertaista funktiota, jonka on tarkoitus laskea luvun itseisarvo. Hieman buginen toteutus saattaa näyttää tältä:
# tiedostossa `my_math.py` def custom_abs(x): """Mukautettu toteutus itseisarvofunktiolle.""" if x < 0: return -x return x
Kirjoitetaan nyt testitiedosto, test_my_math.py
. Ensin perinteinen pytest
-lähestymistapa:
# test_my_math.py (Esimerkkipohjainen) 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
Nämä testit menevät läpi. Funktiomme näyttää oikealta näiden esimerkkien perusteella. Mutta nyt kirjoitetaan ominaisuuspohjainen testi Hypothesiksen avulla. Mikä on itseisarvofunktion ydinominaisuus? Tulos ei saisi koskaan olla negatiivinen.
# test_my_math.py (Ominaisuuspohjainen Hypothesiksella) 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): """Ominaisuus: Minkä tahansa kokonaisluvun itseisarvo on aina >= 0.""" assert custom_abs(x) >= 0
Puretaan tämä osiin:
from hypothesis import given, strategies as st
: Tuomme tarvittavat komponentit.given
on dekoraattori, joka muuttaa tavallisen testifunktion ominaisuuspohjaiseksi testiksi.strategies
on moduuli, josta löydämme datageneraattorimme.@given(st.integers())
: Tämä on testin ydin.@given
-dekoraattori käskee Hypothesista ajamaan tämän testifunktion useita kertoja. Jokaista ajoa varten se generoi arvon käyttämällä annettua strategiaa,st.integers()
, ja välittää sen argumenttinax
testifunktiollemme.assert custom_abs(x) >= 0
: Tämä on ominaisuutemme. Väitämme, että mille tahansa kokonaisluvullex
, jonka Hypothesis keksii, funktiomme tuloksen on oltava suurempi tai yhtä suuri kuin nolla.
Kun ajat tämän pytest
illä, se todennäköisesti menee läpi monilla arvoilla. Hypothesis kokeilee 0, -1, 1, suuria positiivisia lukuja, suuria negatiivisia lukuja ja paljon muuta. Yksinkertainen funktiomme käsittelee kaikki nämä oikein. Kokeillaanpa nyt erilaista strategiaa nähdäksemme, löydämmekö heikkouden.
# Testataan liukuluvuilla @given(st.floats()) def test_abs_floats_property(x): assert custom_abs(x) >= 0
Jos ajat tämän, Hypothesis löytää nopeasti virheellisen tapauksen!
Falsifying example: test_abs_floats_property(x=nan) ... assert custom_abs(nan) >= 0 AssertionError: assert nan >= 0
Hypothesis havaitsi, että kun funktiollemme annetaan float('nan')
(Not a Number), se palauttaa nan
. Väittämä nan >= 0
on epätosi. Olemme juuri löytäneet hienovaraisen bugin, jota emme todennäköisesti olisi osanneet testata manuaalisesti. Voisimme korjata funktiomme käsittelemään tämän tapauksen, ehkä nostamalla ValueError
-poikkeuksen tai palauttamalla tietyn arvon.
Vielä parempi, entä jos bugi olisi ollut hyvin tietyssä liukuluvussa? Hypothesiksen pienennin (shrinker) olisi ottanut suuren, monimutkaisen epäonnistuneen luvun ja pienentänyt sen yksinkertaisimpaan mahdolliseen versioon, joka edelleen laukaisee bugin.
Strategioiden voima: Testidatan muotoilu
Strategiat ovat Hypothesiksen sydän. Ne ovat reseptejä datan generoimiseen. Kirjasto sisältää laajan valikoiman sisäänrakennettuja strategioita, ja voit yhdistellä ja mukauttaa niitä generoidaksesi lähes minkä tahansa kuviteltavissa olevan tietorakenteen.
Yleiset sisäänrakennetut strategiat
- Numeeriset:
st.integers(min_value=0, max_value=1000)
: Generoi kokonaislukuja, valinnaisesti tietyllä välillä.st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
: Generoi liukulukuja, hienojakoisella kontrollilla erikoisarvoihin.st.fractions()
,st.decimals()
- Teksti:
st.text(min_size=1, max_size=50)
: Generoi tietyn pituisia unicode-merkkijonoja.st.text(alphabet='abcdef0123456789')
: Generoi merkkijonoja tietystä merkkijoukosta (esim. heksakoodeille).st.characters()
: Generoi yksittäisiä merkkejä.
- Kokoelmat:
st.lists(st.integers(), min_size=1)
: Generoi listoja, joissa jokainen elementti on kokonaisluku. Huomaa, miten annamme toisen strategian argumenttina! Tätä kutsutaan kompositioiksi.st.tuples(st.text(), st.booleans())
: Generoi tupleja, joilla on kiinteä rakenne.st.sets(st.integers())
st.dictionaries(keys=st.text(), values=st.integers())
: Generoi sanakirjoja määritellyillä avain- ja arvotyypeillä.
- Aika:
st.dates()
,st.times()
,st.datetimes()
,st.timedeltas()
. Näistä voidaan tehdä aikavyöhyketietoisia.
- Sekalaiset:
st.booleans()
: GeneroiTrue
taiFalse
.st.just('constant_value')
: Generoi aina saman yksittäisen arvon. Hyödyllinen monimutkaisten strategioiden koostamisessa.st.one_of(st.integers(), st.text())
: Generoi arvon yhdestä annetuista strategioista.st.none()
: Generoi vainNone
.
Strategioiden yhdistäminen ja muuntaminen
Hypothesiksen todellinen voima tulee sen kyvystä rakentaa monimutkaisia strategioita yksinkertaisemmista.
.map()
-metodin käyttö
.map()
-metodin avulla voit ottaa arvon yhdestä strategiasta ja muuntaa sen joksikin muuksi. Tämä on täydellinen omien luokkien olioiden luomiseen.
# Yksinkertainen dataluokka from dataclasses import dataclass @dataclass class User: user_id: int username: str # Strategia User-olioiden generoimiseksi 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()
ja assume()
Joskus sinun on hylättävä tietyt generoidut arvot. Esimerkiksi saatat tarvita listan kokonaislukuja, joiden summa ei ole nolla. Voisit käyttää .filter()
:
st.lists(st.integers()).filter(lambda x: sum(x) != 0)
Kuitenkin .filter()
-metodin käyttö voi olla tehotonta. Jos ehto on usein epätosi, Hypothesis saattaa käyttää paljon aikaa yrittäessään generoida kelvollista esimerkkiä. Parempi lähestymistapa on usein käyttää assume()
-funktiota testifunktion sisällä:
from hypothesis import assume @given(st.lists(st.integers())) def test_something_with_non_zero_sum_list(numbers): assume(sum(numbers) != 0) # ... testilogiikkasi täällä ...
assume()
kertoo Hypothesikselle: "Jos tämä ehto ei täyty, hylkää tämä esimerkki ja kokeile uutta." Se on suorempi ja usein suorituskykyisempi tapa rajoittaa testidataasi.
st.composite()
-funktion käyttö
Todella monimutkaiseen datan generointiin, jossa yksi generoitu arvo riippuu toisesta, st.composite()
on tarvitsemasi työkalu. Sen avulla voit kirjoittaa funktion, joka ottaa argumentikseen erityisen draw
-funktion, jota voit käyttää arvojen noutamiseen muista strategioista askel askeleelta.
Klassinen esimerkki on listan ja siihen kelvollisen indeksin generointi.
@st.composite def list_and_index(draw): # Ensin, nouda ei-tyhjä lista my_list = draw(st.lists(st.integers(), min_size=1)) # Sitten, nouda indeksi, joka on taatusti kelvollinen kyseiselle listalle 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 # Tämä haku on taatusti turvallinen strategian rakenteen ansiosta element = my_list[index] assert element is not None # Yksinkertainen väittämä
Hypothesis toiminnassa: Tosielämän skenaarioita
Sovellamme näitä konsepteja realistisempiin ongelmiin, joita ohjelmistokehittäjät kohtaavat päivittäin.
Skenaario 1: Datan serialisointifunktion testaaminen
Kuvittele funktio, joka serialisoi käyttäjäprofiilin (sanakirjan) URL-turvalliseksi merkkijonoksi ja toinen, joka deserialisoi sen. Keskeinen ominaisuus on, että prosessin tulisi olla täysin käänteinen.
import json import base64 def serialize_profile(data: dict) -> str: """Serialisoi sanakirjan URL-turvalliseksi base64-merkkijonoksi.""" json_string = json.dumps(data) return base64.urlsafe_b64encode(json_string.encode('utf-8')).decode('utf-8') def deserialize_profile(encoded_str: str) -> dict: """Deserialisoi merkkijonon takaisin sanakirjaksi.""" json_string = base64.urlsafe_b64decode(encoded_str.encode('utf-8')).decode('utf-8') return json.loads(json_string) # Nyt testi # Tarvitsemme strategian, joka generoi JSON-yhteensopivia sanakirjoja 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): """Ominaisuus: Koodatun profiilin deserialisoinnin tulisi palauttaa alkuperäinen profiili.""" encoded = serialize_profile(profile) decoded = deserialize_profile(encoded) assert profile == decoded
Tämä yksittäinen testi pommittaa funktioitamme valtavalla määrällä erilaista dataa: tyhjiä sanakirjoja, sanakirjoja sisäkkäisillä listoilla, sanakirjoja unicode-merkeillä, sanakirjoja oudoilla avaimilla ja paljon muuta. Se on paljon perusteellisempi kuin muutaman manuaalisen esimerkin kirjoittaminen.
Skenaario 2: Lajittelualgoritmin testaaminen
Palataan lajitteluesimerkkiimme. Näin testaisit aiemmin määrittelemiämme ominaisuuksia.
from collections import Counter def my_buggy_sort(numbers): # Lisätään hienovarainen bugi: se pudottaa duplikaatit return sorted(list(set(numbers))) @given(st.lists(st.integers())) def test_sorting_properties(numbers): sorted_list = my_buggy_sort(numbers) # Ominaisuus 1: Tuloste on järjestetty for i in range(len(sorted_list) - 1): assert sorted_list[i] <= sorted_list[i+1] # Ominaisuus 2: Elementit ovat samat (tämä löytää bugin) assert Counter(numbers) == Counter(sorted_list) # Ominaisuus 3: Funktio on idempotenttinen assert my_buggy_sort(sorted_list) == sorted_list
Kun ajat tämän testin, Hypothesis löytää nopeasti virheellisen esimerkin ominaisuudelle 2, kuten numbers=[0, 0]
. Funktiomme palauttaa [0]
, ja Counter([0, 0])
ei ole yhtä suuri kuin Counter([0])
. Pienennin varmistaa, että virheellinen esimerkki on mahdollisimman yksinkertainen, mikä tekee bugin syystä heti ilmeisen.
Skenaario 3: Tilallinen testaus
Olioille, joilla on sisäinen tila, joka muuttuu ajan myötä (kuten tietokantayhteys, ostoskori tai välimuisti), bugien löytäminen voi olla uskomattoman vaikeaa. Virheen laukaisemiseen saattaa tarvita tietyn toimintosarjan. Hypothesis tarjoaa RuleBasedStateMachine
-luokan juuri tähän tarkoitukseen.
Kuvittele yksinkertainen API muistissa olevalle avain-arvo-tietovarastolle:
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)
Voimme mallintaa sen käyttäytymistä ja testata sitä tilakoneella:
from hypothesis.stateful import RuleBasedStateMachine, rule, Bundle class KeyValueStoreMachine(RuleBasedStateMachine): def __init__(self): super().__init__() self.model = {} self.sut = SimpleKeyValueStore() # Bundle()-funktiota käytetään datan välittämiseen sääntöjen välillä 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() # Testin ajamiseksi peritään luokka koneesta ja unittest.TestCase:sta # Pytestissä voit yksinkertaisesti määrittää testin kone-luokalle TestKeyValueStore = KeyValueStoreMachine.TestCase
Hypothesis suorittaa nyt satunnaisia sarjoja set_key
-, delete_key
-, get_key
- ja check_size
-operaatioita yrittäen säälimättä löytää sarjan, joka saa jonkin väittämistä epäonnistumaan. Se tarkistaa, toimiiko poistetun avaimen hakeminen oikein, onko koko johdonmukainen useiden asetus- ja poistotoimintojen jälkeen, ja monia muita skenaarioita, joita et ehkä tulisi ajatelleeksi testata manuaalisesti.
Parhaat käytännöt ja edistyneet vinkit
- Esimerkkitietokanta: Hypothesis on älykäs. Kun se löytää bugin, se tallentaa virheellisen esimerkin paikalliseen hakemistoon (
.hypothesis/
). Seuraavan kerran, kun ajat testisi, se toistaa ensin tuon virheellisen esimerkin, antaen sinulle välitöntä palautetta siitä, että bugi on edelleen olemassa. Kun korjaat sen, esimerkkiä ei enää toisteta. - Testin suorituksen hallinta
@settings
-dekoraattorilla: Voit hallita monia testiajon osa-alueita käyttämällä@settings
-dekoraattoria. Voit lisätä esimerkkien määrää, asettaa aikarajan sille, kuinka kauan yksittäinen esimerkki voi kestää (ikuisten silmukoiden kiinnisaamiseksi), ja poistaa käytöstä tiettyjä kuntotarkistuksia.@settings(max_examples=500, deadline=1000) # Aja 500 esimerkkiä, 1 sekunnin aikaraja @given(...) ...
- Virheiden toistaminen: Jokainen Hypothesiksen ajo tulostaa siemenarvon (esim.
@reproduce_failure('version', 'seed')
). Jos CI-palvelin löytää bugin, jota et voi toistaa paikallisesti, voit käyttää tätä dekoraattoria annetulla siemenarvolla pakottaaksesi Hypothesiksen ajamaan täsmälleen saman esimerkkisarjan. - Integrointi CI/CD-putkeen: Hypothesis sopii täydellisesti mihin tahansa jatkuvan integraation putkeen. Sen kyky löytää hämäriä bugeja ennen kuin ne pääsevät tuotantoon tekee siitä korvaamattoman turvaverkon.
Ajattelutavan muutos: Ominaisuuksina ajattelu
Hypothesiksen omaksuminen on enemmän kuin vain uuden kirjaston oppimista; se on uuden tavan omaksumista ajatella koodisi oikeellisuutta. Sen sijaan, että kysyisit: "Mitä syötteitä minun pitäisi testata?", alat kysyä: "Mitkä ovat tämän koodin yleismaailmalliset totuudet?"
Tässä on joitain kysymyksiä, jotka ohjaavat sinua ominaisuuksien tunnistamisessa:
- Onko olemassa käänteinen operaatio? (esim. serialisointi/deserialisointi, salaus/purku, pakkaus/purku). Ominaisuus on, että operaation ja sen käänteisoperaation suorittamisen tulisi tuottaa alkuperäinen syöte.
- Onko operaatio idempotenttinen? (esim.
abs(abs(x)) == abs(x)
). Funktion soveltaminen useammin kuin kerran tuottaa saman tuloksen kuin sen soveltaminen kerran. - Onko olemassa toinen, yksinkertaisempi tapa laskea sama tulos? Voit testata, että monimutkainen, optimoitu funktiosi tuottaa saman tuloksen kuin yksinkertainen, ilmeisen oikea versio (esim. testaamalla hienoa lajitteluasi Pythonin sisäänrakennettua
sorted()
-funktiota vastaan). - Minkä pitäisi aina olla totta tulosteesta? (esim.
find_prime_factors
-funktion tuloste saisi sisältää vain alkulukuja, ja niiden tulon tulisi olla yhtä suuri kuin syöte). - Miten tila muuttuu? (Tilallinen testaus) Mitkä invariantit on säilytettävä minkä tahansa kelvollisen operaation jälkeen? (esim. Ostoskorissa olevien tuotteiden määrä ei voi koskaan olla negatiivinen).
Johtopäätös: Uusi luottamuksen taso
Ominaisuuspohjainen testaus Hypothesiksella ei korvaa esimerkkipohjaista testausta. Tarvitset edelleen tiettyjä, käsin kirjoitettuja testejä kriittiselle liiketoimintalogiikalle ja hyvin ymmärretyille vaatimuksille (esim. "Maan X käyttäjän on nähtävä hinta Y").
Mitä Hypothesis tarjoaa, on tehokas, automatisoitu tapa tutkia koodisi käyttäytymistä ja suojautua odottamattomilta reunatapauksilta. Se toimii väsymättömänä kumppanina, joka generoi tuhansia testejä, jotka ovat monipuolisempia ja ovelampia kuin kukaan ihminen voisi realistisesti kirjoittaa. Määrittelemällä koodisi perusominaisuudet luot vankan määrittelyn, jota vastaan Hypothesis voi testata, antaen sinulle uuden tason luottamusta ohjelmistoosi.
Seuraavan kerran kun kirjoitat funktion, pysähdy hetkeksi miettimään esimerkkien tuolle puolen. Kysy itseltäsi: "Mitkä ovat säännöt? Minkä on aina oltava totta?" Anna sitten Hypothesiksen tehdä kova työ yrittäessään rikkoa niitä. Tulet yllättymään siitä, mitä se löytää, ja koodisi on parempi sen ansiosta.