Lietuvių

Susipažinkite su savybėmis pagrįstu testavimu per praktinį „QuickCheck“ diegimą. Patobulinkite savo testavimo strategijas patikimomis, automatizuotomis technikomis patikimesnei programinei įrangai.

Savybėmis pagrįsto testavimo įvaldymas: „QuickCheck“ diegimo vadovas

Šiuolaikiniame sudėtingame programinės įrangos pasaulyje tradicinis vienetų testavimas (angl. unit testing), nors ir vertingas, dažnai yra nepakankamas atrandant subtilias klaidas ir kraštutinius atvejus. Savybėmis pagrįstas testavimas (angl. Property-based testing, PBT) siūlo galingą alternatyvą ir papildymą, perkeliant dėmesį nuo pavyzdžiais pagrįstų testų prie savybių, kurios turėtų būti teisingos plačiam įvesties duomenų spektrui, apibrėžimo. Šiame vadove nuodugniai nagrinėjamas savybėmis pagrįstas testavimas, ypač daug dėmesio skiriant praktiniam diegimui naudojant „QuickCheck“ stiliaus bibliotekas.

Kas yra savybėmis pagrįstas testavimas?

Savybėmis pagrįstas testavimas (PBT), dar vadinamas generatyviuoju testavimu, yra programinės įrangos testavimo technika, kai jūs apibrėžiate savybes, kurias jūsų kodas turėtų atitikti, o ne pateikiate konkrečius įvesties ir išvesties pavyzdžius. Testavimo karkasas (angl. framework) automatiškai sugeneruoja daugybę atsitiktinių įvesties duomenų ir patikrina, ar šios savybės galioja. Jei kuri nors savybė neatitinka, karkasas bando sumažinti (angl. shrink) netinkamus įvesties duomenis iki minimalaus, atkuriamo pavyzdžio.

Pagalvokite apie tai taip: užuot sakę „jei funkcijai pateiksiu įvestį 'X', tikiuosi gauti išvestį 'Y'“, jūs sakote „nesvarbu, kokią įvestį pateiksiu šiai funkcijai (laikantis tam tikrų apribojimų), šis teiginys (savybė) visada turi būti teisingas“.

Savybėmis pagrįsto testavimo privalumai:

„QuickCheck“: pradininkas

„QuickCheck“, iš pradžių sukurta „Haskell“ programavimo kalbai, yra žinomiausia ir įtakingiausia savybėmis pagrįsto testavimo biblioteka. Ji suteikia deklaratyvų būdą apibrėžti savybes ir automatiškai generuoja testavimo duomenis joms patikrinti. „QuickCheck“ sėkmė įkvėpė daugybę diegimų kitose kalbose, kurios dažnai pasiskolino „QuickCheck“ pavadinimą arba pagrindinius principus.

Pagrindiniai „QuickCheck“ stiliaus diegimo komponentai yra:

Praktinis „QuickCheck“ diegimas (konceptualus pavyzdys)

Nors visas diegimas nepatenka į šio dokumento apimtį, pailiustruokime pagrindines sąvokas supaprastintu, konceptualiu pavyzdžiu, naudojant hipotetinę „Python“ tipo sintaksę. Sutelksime dėmesį į funkciją, kuri apverčia sąrašą.

1. Apibrėžkite testuojamą funkciją


def reverse_list(lst):
  return lst[::-1]

2. Apibrėžkite savybes

Kokias savybes turėtų atitikti `reverse_list`? Štai kelios iš jų:

3. Apibrėžkite generatorius (hipotetinius)

Mums reikia būdo generuoti atsitiktinius sąrašus. Tarkime, turime `generate_list` funkciją, kuri kaip argumentą priima maksimalų ilgį ir grąžina atsitiktinių sveikųjų skaičių sąrašą.


# Hipotetinė generatoriaus funkcija
def generate_list(max_length):
  length = random.randint(0, max_length)
  return [random.randint(-100, 100) for _ in range(length)]

4. Apibrėžkite testų paleidiklį (hipotetinį)


# Hipotetinis testų paleidiklis
def quickcheck(property, generator, num_tests=1000):
  for _ in range(num_tests):
    input_value = generator()
    try:
      result = property(input_value)
      if not result:
        print(f"Savybė neatitiko įvesčiai: {input_value}")
        # Bandoma sumažinti įvestį (čia nediegiama)
        break # Paprastumo dėlei sustojama po pirmos nesėkmės
    except Exception as e:
      print(f"Įvyko išimtis įvesčiai: {input_value}: {e}")
      break
  else:
    print("Savybė atitiko visus testus!")

5. Parašykite testus

Dabar galime naudoti savo hipotetinį karkasą testams parašyti:


# 1 savybė: Dukart apvertus grąžinamas pradinis sąrašas
def property_reverse_twice(lst):
  return reverse_list(reverse_list(lst)) == lst

# 2 savybė: Apversto sąrašo ilgis yra toks pat kaip pradinio
def property_length_preserved(lst):
  return len(reverse_list(lst)) == len(lst)

# 3 savybė: Apvertus tuščią sąrašą grąžinamas tuščias sąrašas
def property_empty_list(lst):
    return reverse_list([]) == []

# Paleiskite testus
quickcheck(property_reverse_twice, lambda: generate_list(20))
quickcheck(property_length_preserved, lambda: generate_list(20))
quickcheck(property_empty_list, lambda: generate_list(0))  #Visada tuščias sąrašas

Svarbi pastaba: Tai yra labai supaprastintas pavyzdys iliustracijai. Realaus pasaulio „QuickCheck“ diegimai yra sudėtingesni ir suteikia tokias funkcijas kaip mažinimas (angl. shrinking), pažangesni generatoriai ir geresnis klaidų pranešimas.

„QuickCheck“ diegimai įvairiose kalbose

„QuickCheck“ koncepcija buvo perkelta į daugybę programavimo kalbų. Štai keletas populiarių diegimų:

Diegimo pasirinkimas priklauso nuo jūsų programavimo kalbos ir testavimo karkaso parinkčių.

Pavyzdys: „Hypothesis“ naudojimas („Python“)

Pažvelkime į konkretesnį pavyzdį naudojant „Hypothesis“ biblioteką „Python“ kalboje. „Hypothesis“ yra galinga ir lanksti savybėmis pagrįsto testavimo biblioteka.


from hypothesis import given
from hypothesis.strategies import lists, integers

def reverse_list(lst):
  return lst[::-1]

@given(lists(integers()))
def test_reverse_twice(lst):
  assert reverse_list(reverse_list(lst)) == lst

@given(lists(integers()))
def test_reverse_length(lst):
  assert len(reverse_list(lst)) == len(lst)

@given(lists(integers()))
def test_reverse_empty(lst):
    if not lst:
        assert reverse_list(lst) == lst


#Norėdami paleisti testus, vykdykite pytest
#Pavyzdys: pytest jusu_testu_failas.py

Paaiškinimas:

Kai paleidžiate šį testą su `pytest` (įdiegę „Hypothesis“), „Hypothesis“ automatiškai sugeneruos daugybę atsitiktinių sąrašų ir patikrins, ar savybės galioja. Jei kuri nors savybė neatitiks, „Hypothesis“ bandys sumažinti netinkamą įvestį iki minimalaus pavyzdžio.

Pažangios savybėmis pagrįsto testavimo technikos

Be pagrindų, yra keletas pažangių technikų, kurios gali dar labiau patobulinti jūsų savybėmis pagrįsto testavimo strategijas:

1. Pasirinktiniai generatoriai

Sudėtingiems duomenų tipams ar specifiniams srities reikalavimams dažnai reikės apibrėžti pasirinktinius generatorius. Šie generatoriai turėtų kurti galiojančius ir reprezentatyvius duomenis jūsų sistemai. Tam gali prireikti naudoti sudėtingesnį algoritmą duomenims generuoti, kad atitiktų specifinius jūsų savybių reikalavimus ir išvengtumėte tik nenaudingų ir klaidingų testų atvejų generavimo.

Pavyzdys: Jei testuojate datos analizavimo funkciją, jums gali prireikti pasirinktinio generatoriaus, kuris generuotų galiojančias datas tam tikrame diapazone.

2. Prielaidos

Kartais savybės galioja tik tam tikromis sąlygomis. Galite naudoti prielaidas, kad nurodytumėte testavimo karkasui atmesti įvestis, kurios neatitinka šių sąlygų. Tai padeda sutelkti testavimo pastangas į svarbias įvestis.

Pavyzdys: Jei testuojate funkciją, kuri skaičiuoja skaičių sąrašo vidurkį, galite daryti prielaidą, kad sąrašas nėra tuščias.

„Hypothesis“ bibliotekoje prielaidos įgyvendinamos naudojant `hypothesis.assume()`:


from hypothesis import given, assume
from hypothesis.strategies import lists, integers

@given(lists(integers()))
def test_average(numbers):
  assume(len(numbers) > 0)
  average = sum(numbers) / len(numbers)
  # Patvirtinkite kažką apie vidurkį
  ...

3. Būsenų mašinos

Būsenų mašinos yra naudingos testuojant būseną turinčias sistemas, pavyzdžiui, vartotojo sąsajas ar tinklo protokolus. Jūs apibrėžiate galimas sistemos būsenas ir perėjimus, o testavimo karkasas generuoja veiksmų sekas, kurios perveda sistemą per skirtingas būsenas. Savybės tada patikrina, ar sistema kiekvienoje būsenoje elgiasi teisingai.

4. Savybių derinimas

Galite sujungti kelias savybes į vieną testą, kad išreikštumėte sudėtingesnius reikalavimus. Tai gali padėti sumažinti kodo dubliavimąsi ir pagerinti bendrą testų aprėptį.

5. Aprėptimi pagrįstas „fuzzing“ testavimas

Kai kurie savybėmis pagrįsto testavimo įrankiai integruojasi su aprėptimi pagrįstomis „fuzzing“ technikomis. Tai leidžia testavimo karkasui dinamiškai koreguoti generuojamas įvestis, siekiant maksimaliai padidinti kodo aprėptį ir galbūt atskleisti gilesnes klaidas.

Kada naudoti savybėmis pagrįstą testavimą

Savybėmis pagrįstas testavimas nėra tradicinio vienetų testavimo pakaitalas, o greičiau papildanti technika. Jis ypač tinka:

Tačiau PBT gali būti ne pats geriausias pasirinkimas labai paprastoms funkcijoms su vos keliomis galimomis įvestimis arba kai sąveika su išorinėmis sistemomis yra sudėtinga ir sunkiai imituojama (angl. mock).

Dažniausios klaidos ir gerosios praktikos

Nors savybėmis pagrįstas testavimas teikia didelių privalumų, svarbu žinoti galimas klaidas ir laikytis gerųjų praktikų:

Išvada

Savybėmis pagrįstas testavimas, kurio ištakos siekia „QuickCheck“, yra reikšmingas žingsnis į priekį programinės įrangos testavimo metodologijose. Perkeldami dėmesį nuo konkrečių pavyzdžių prie bendrų savybių, programuotojai gali atskleisti paslėptas klaidas, pagerinti kodo dizainą ir padidinti pasitikėjimą savo programinės įrangos teisingumu. Nors PBT įvaldymas reikalauja mąstysenos pokyčių ir gilesnio sistemos elgsenos supratimo, nauda, susijusi su geresne programinės įrangos kokybe ir sumažėjusiomis priežiūros išlaidomis, yra verta pastangų.

Nesvarbu, ar dirbate su sudėtingu algoritmu, duomenų apdorojimo konvejeriu, ar būseną turinčia sistema, apsvarstykite galimybę įtraukti savybėmis pagrįstą testavimą į savo testavimo strategiją. Išnagrinėkite „QuickCheck“ diegimus, pasiekiamus jūsų pageidaujamoje programavimo kalboje, ir pradėkite apibrėžti savybes, kurios atspindi jūsų kodo esmę. Tikėtina, kad nustebsite dėl subtilių klaidų ir kraštutinių atvejų, kuriuos PBT gali atskleisti, o tai padės sukurti tvirtesnę ir patikimesnę programinę įrangą.

Taikydami savybėmis pagrįstą testavimą, galite pereiti nuo paprasto patikrinimo, ar jūsų kodas veikia kaip tikėtasi, prie įrodymo, kad jis veikia teisingai esant daugybei įvairių galimybių.