Automatsko testiranje code-a

Upravo mi sinula genijalna ideja (valjda je genijalna xd) …pa reko da sheram.

Znači, većina se nas (programera) vjerovatno susrela sa testovima za testiranje codea. No testiranje je mukotrpno iz razloga što zahtjeva ne tako malo vrijeme za pisanje testova.
Pogotovo ako želimo napisati testove za poveće libriray-e, tu onda ima ohoho posla da se sve pokrije testovima.

No upravo mi sinula ideja kako bi se code mogao testirati bez da itko piše testove. :smiley:
Nešto mi pari da ovakvo nešto već sigurno postoji, jer je zapravo stvar poprilično i jednostavna …ali eto teme da diskutiramo.

Za početak ću šutit, pa ako netko ima ideju kako bi se to moglo napraviti ili znanje kako se to radi, neka napiše. :wink:

žž.

3 Likeova

Pa zagonetka za razbibrigu, da ne pokvarim odmah. :slight_smile:

Ne, nego sve ideje™ koje si nudio eonima kasne za onim što poslovično zovemo industrijski standard.
Pa sam nestrpljiv da vidim šta je sljedeće zasjalo (da ne kažem šta si [tek] sad naučio).
Razlog za ovo je poprilično jednostavan (i čak vrlo rješiv): ne čitaš [dokumentaciju i relevantne/aktualne artikle] a to u ovom poslu ne prolazi.

1 Like

Sutim i ja :slight_smile:

Nego zanima me na koji tip testova tacno mislis, kad kazes testiranje koda ?
Iskreno volio bih da takvo nesto postoji, jer mi je pisanje E2E testova jako dosadno i naporno.

I mene zanima, ali ne cekam bas dugo, ne visim bas satima i radije ciljano pisem. E sad, zadnji puta sto se sjecam mi je tema koju si pokrenuo isto bila zanimljiva, ali mi se nije dalo igrat te igrice pogađanja pa sam odustao i nisam pratio, steta.

Stotine bezveznih postova za 4-5 dobra. Gubljenje vremena.

Iskreno, nebih znao po kojem imenu se vode…pošto ne znam kako se stručno koji testovi klasificiraju imenom.

Mislim uglavnom na testove gdje se testira neka metoda koja je “nemutabilna”, što će reći da svaki puta za isti ulaz mora dati isti izlaz.

Pisanje takvih testova se uglavnom svodi na popisivanje mapa “ulaza/izlaza” …gdje test prilikom svog izvršavanja hrani te metode zadanim ulazima sa mape i provjerava dali izlaz sa metode odgovara izlazu sa mape.

I točno taj dio se može automatizirati…kreiranje tih mapa.

Znači cijela ideja je u tome da se postavi da neka metoda samostalno kreira svoju mapu ulaza/izlaza.

No kako će metoda znati koji su očekivani izlazi za odgovarajuće ulaze? Pa metoda to ne može znati …ali može arhivirati svaki puta koji je imala ulaz i koji je vratila izlaz. Zatim te podatke sprema u svoju mapu.

Kada se zavrti test, metoda će zaviriti u svoju mapu i izvrtit će testove za sve arhivirane ulaze…te će dobivene izlaze provjeriti da li odgovaraju izlazima koji su arhivirani u mapi.

Ako je u međuvremenu napisan code koji nije backward kompatibilan, ovakvi automatski testovi će to lako detektirati i pokazati. Tj. pokazati će da se trenutna prijenosna funkcija ulaza/izlaza po nečemu pošeremetila od one koja je bila ranije, tj. one koja je automatski arhivirana u mapi.

Tada naravno developer može samostalno odlučiti hoće li novu prijenosnu funkciju prihvatiti ispravnom, te obrisati stari podatak ulaz/izlaz iz mape …ili će mu pucanje testa jednostavno ukazati da se nešto u codu promjenilo što se nije smjelo promjeniti.

Također, valja primjetiti da sam proces automatskog arhiviranja ulaza/izlaza ne mora raditi non stop (logično, radi optimizacijskih razloga) …nego je dovoljno da radi u develop modu.
Ionako u develop mode-u kada se doda novi feature…developer često proba malo edge-cesove itd. Samo to probanje kako funkcija radi će automatski kreirati mapu ulaza/izlaza za taj range parametara koji su se prilikom toga isprobavanja odigrali.
Što je ekvivalent tome da je developer ručno unio taj range parametara prilikom pisanja testova…upravo smo preskočili/automatizirali ono što kažeš da je naporno i dosadno.

S time da i nakon tog inicijalnog probanja rada neke sekcije programa…funkcija će i dalje povremeno biti trigirana u develop okruženju. (jer logično, svaka funkcija tu i tamo bude trigirana)…te će tako
konstantno razvijati svoju mapu ulaza/izlaza. Isto kao što će prilikom tog razvijanja vrisnuti u bilo kojem momentu ako uoči da je dobila već postojeći par ulaza, a da se izlaz ne poklapa sa onim što mapa govori da bi trebalo biti na izlazu.

Znači proces koji se može praktički u potpunosti automatizirati. :slight_smile:

Jedino što metodama koje se želi uključiti u testove treba dodati nekakve registere na ulazu i izlazu koji će voditi spomenutu mapu.

Kada bi programski jezik na kojem se ovo ugrađuje imao nekakav globalni event koji se trigira kod početka i završetka trigiranja svake funkcije, onda se nebi trebali ugrađivati niti spomenuti registeri…nego bi se moglo sve hendlati iz tog globalnog eventa.

Simple as that. :slight_smile:

Koliko ja vidim ti pricas o unit testovima.

To se zove pure funkcija a pure funkcija je funkcija koja za isti input uvijek daje isti output (ono sto si vec rekao) i jos jako bitno nema side efekte.

Ako ti imas ovakvu funkciju, koja je pure
const add = (x, y) => x + y;

i pretvoris je u nesto ovakvo, radi testiranja

const add = (x, y) =>  {
   const output = x + y;
   saveToMap({x, y}, output)
   return output;
}

Onda ta funkcija vise nije pure :slight_smile: Samim tim sto nije pure ona vjerovatno i radi vise nego sto je potrebno.
Pored toga ovim pristupom imas coupling izmedju implementacije (glavnog dijela) i neceg sporednog u ovom slucaju test.

Ok recimo da sve to radi na tvojoj masini, kad ti pozivas funkcije itd, medjutim sta je sa CI/CD procesima ? Kako ce se to ponasati u nekom drugom okruzenju ?

Unit testove vidim korisnima jedino u slucaju kad radim TDD - prvo test, pa onda pisi implementaciju jer sam vidio da je test failao, ali uz to sam vidio i da sam ga i popravio.Iskreno ne vidim puno vrijednosti u pisanju unit testova naknadno, tj ne daje mi previse sigurnosti.

Mislim da ovo sto si ti opisao ne postoji, bar ja ne poznajem neko slicno rijesenje.

Naravno, receno vazi pod pretpostavkom da sam razumio dobro tvoje rijesenje.

Svaka funkcija ima side efekte…samo neke ti ne vidiš, ali ih vidi engine koji izvrsava code.
Ali pošto ti side efekti ne mogu/nebi smjeli izmjenit ponašanje funkcije…onda za nas nisu bitni.

Tako i ovaj side efekt nije bitan za testiranje ponašanja funkcije…ako se posloži da ne može utjecat na nju.

Kad se prica o pure funkcijama, prica se o implementacijskom kodu.Mene apsolutno ne zanima sta V8 radi sa mojim kodom i kako ga on vidi.

Kako bi ti to poslozio ?

Poanta je bila u tome da te onda u svrhu unit testiranja isto tako apsolutno ne trebaju zanimati side efekti koji ne mogu izmjeniti ponašanje funkcije koja sadrzi side efekt i koji također ne mogu izmjeniti niti ponašanje okolnih funkcija.

A takve side efekte koji na ništa ne ostavljaju efekt …ja ih nebi niti zvao side efektima. :slight_smile:

To što će registar ulaza i izlaza negdje mapirati iste, neće utjecat na ponašanje iti jedne funkcije. Osim što će ih malo usporit u izvrsavanju.

Definicija side effecta

Side Effects

A side effect is any application state change that is observable outside the called function other than its return value. Side effects include:

  • Modifying any external variable or object property (e.g., a global variable, or a variable in the parent function scope chain)
  • Logging to the console
  • Writing to the screen
  • Writing to a file
  • Writing to the network
  • Triggering any external process
  • Calling any other functions with side-effects

I mislim da ovo poredjenje sa V8 nema bas puno smisla, ali hajde :slight_smile:
Mozes ti naravno imati neke svoje definicije side-effecta, ali obicni console.log unutar funkcije je tretiran kao side-effect.

Mozes li mi samo reci da li ce to izgledati nesto slicno onome sto sam ja napisao u jednom od prethonih postova (add funkcija koja poziva saveToMap funkciju) ? Ako ne, volio bih barem vidjeti malo nekog koda.

Mogu ljudi stvarati definicije koje god zele…i to je ok.

Ja samo zelim reći da ako funkcija utječe na nešto izvan sebe što ne može utjecati na odnos ulaza/izlaza nje same, niti okolnih funkcija (koje zelimo testirat), onda to neće smetati za testiranje codea. Simple as that.

Namjerno u tekstu povrh ne koristim riječ “side efekt” tako da nema potrebe da se ljepimo na definicije…nego da uočimo što gornji tekst želi reći sam za sebe. Svaka riječ u tekstu je dovoljno jasna i nema potrebe da se značenje i smisao istoga dodatno pokušava opovrgnut nekim definicijama.

Što se tiče ostalog…otišao si u širinu, pa ću te slijedit…
…što se tiče definicije side-efekta u kontekstu problematike testiranja. Ona reče da je side efekt ako funkcija piše po nekom vanjskom fileu. I to je istina.
Ali treba razmislit dali to pisanje utječe na ikoju drugu funkciju? Ako bi neka druga funkcija svoju logiku bazirala na čitanju iz tog filea…ili na prebrojavanju zauzeća diska …onda smo sa tim side efektom dotakli tu drugu funkciju, u suprotnom nismo.

Treba naravno bit oprezan kod takvih zakljucaka jel tko koga dotiče, al side efekt nije problematičan sam po sebi…problematično je upravo to ako on posljedično izmjeni ponašanje neke druge funkcije. Jer tad smo naptavili EFEKT na nekoga. Ali na nekoga BITNOGA.
Jer ako ćemo gledati sve efekte koje ostavlja funkcija svojim radom…onda ih ima nebrojeno:

  • izmjena error_log file-a
  • zagrijavanje procesora
  • punjenje garbage kolektora
  • pa eto, može trigiranje neke funkcije zabljesnut monitor i uplašit ptičicu sastrane. I to je side effekt…i što je najgore taj može imat i posljedicu na rad programa ako ptičica sleti na tipkovnicu na enter tipku i abortira rad programa.

Karikiram…poanta je da ne mozemo gledati slijepo u definiciju i ne razmisliti koji side efekt je bitan, koji nije.
Oni su u definiciji popisali najbitnije efekte, ali nije svaki od njih kritičan u svakom obliku. Velim, pisanje po fileu nije kritično, ako to ne modulira rad drugih.
Nakraju krajeva, svaka funkcija moze trigirati da se error log file izmjeni…pa bi po time ispalo da praktički niti nema “pure” codea bez side efekta.

Moze i ne mora. Ako u konkretnom programskom jeziku postoji event da se zakacis na ta mjesta…onda se moze tako. U suprotnom se mogu zalijepit registri otprilike kao sto si napisao. S time da bi dodao opasku da nema smisla da se registar odmah zove “saveToMap”. Registar će tek unutar sebe odlučivati kada će spremat u mapu, a kada ne. A metoda “saveToMap” ne radi nista drugo, negol samo spremanje u mapu kada joj se to naredi iz registra.

Nadalje, ako ne postoji event da se zakaci na ulaze/izlae funkcija, ali code se piše ispred nekakvog loadera/kompajlera nad kojim imamo kontrolu…onda se možemo i pomoću kompajlera zakačit na svaku funkciju. U tom slučaju ne moramo blatit code sa dodavanjem nekakvih registera, nego će to kompajler napraviti za nas. Pa mi zadržavamo clean code, a registeri će ipak bit tu.
Logično je u tom slučaju bolje ugradit sa kompajlerom evente na ulaz/izlaz svake funkcije (ako to već programski jezik nativno nije omogućio), pa kasnije po želji na te evente zakačit registere za mapiranje ulaza/izlaza …al po želji nam ostaju eventi i za kakve druge radnje. :slight_smile:
Ovo je najfleksibilnija solucija … al treba imat kontrolu nad kompajlerom za takve stvari. Zato sam i pisao u onoj temi o endless mogućnostima koje nam se otvaraju kad se upletemo u kompajliranje codea.
Osobno ću ovo rješiti na razini kompajlera.

I sad još jedna zanimljiva opaska. Neka se taj kompajler zove B8 i nek jednog dana dođe neki drugi belmin i tvrdi:

Mene apsolutno ne zanima sta B8 radi sa mojim kodom i kako ga on vidi.

Hoćem reći da se side efekti najnormalnije dešavaju i na razini kompajlera. I nema razloga da su ti oni manje ili više bitni od side efekata koje sam uneseš i koji nemaju na nikoga utjecaj…kao što ovi iz kompajlera nemaju na nikoga utjecaj.

I nije niti skroz istina da ovi iz kompajlera nemaju na nikog utjecaj. Baš u javascriptu imaš problem/bug kod pražnjenja garbage kolektora…pa ako unutar funkcije kreiraš lokalne varijable…one neće baš nestat nakon završetka funkcije…nego će zaglavit u RAMu. Problem je nebitan kod većine aplikacija gdje se funkcije pozovu relativno malo puta…ali kod igrica koje se vrte minimalno 23 frame per second, često i 60fps …pa se cijela logika računanja koordinata i svega mora vrtit 60 puta u sekundi…i tako konstantno…
…e tu bome krivo pražnjenje garbage kolektora hoće zakucat RAM do kraja i onda igra počme trzati i štekati.

To je bogme vidni side efekt funkcija za koje ti reče da nemaju side efekt jer te baš briga što je ispod haube.

Dal gornji side effekt može utjecat na logiku ostalih funkcija …ili će samo usporit rad igre?
Pa zapravo može i modulirat/izmjenit rad ostalih funkcija. Recimo možeš imati funkciju koja dinamički određuje koliki će bit FPS, pa ako igra krene štekat FPS se automatski smanji. Što će reć da funkcije koje po tvojoj definiciji nisu mogle napravit side effekt na ostale funkcije, u praksi su mogle…i to preko garbage kolektora i neočekivanog zakucavanja RAM memorije.

Poanta…sve što se napravi može biti jednog dana za nekog drugog “ispod haube” …ako se tako ukomponira u code. Što ne znači da i ti djelovi ne mogu imati svoje mane ili side efekte.

Ne da mi se stvarno ulaziti u filozofske diskusije s tobom :smiley: Jasno sam vise puta ponovio, da pricam o implementacijskom kodu a ne da li se procesor zagrijao :sweat_smile:Zna se na sta se misli kad se kaze side efekat, tako da mislim da stvarno nema smisla praviti ovakva poredjenja.

Sta bi bilo, kad’ bi bilo.

Nisi mi odgovorio na pitanje o CI/CD, kako ces tamo izvrsiti testove ? Naravno da neces uplodati mape koje su generisane na tvojoj masini.

Svjestan si da pisanje u file moze failati iz bilo kojeg razloga, sta ces u tom slucaju uraditi ? Sta u slucaju da se pisanje/citanje iz mape odvija jako sporo ?

Kao sto sam vec obrazlozio, meni unit testiranje ima smisla jedino kad radim TDD.Ako nisi vidio crven test, kako mozes biti siguran da sve radi kako treba.Samim tim ovaj tvoj pristup mi je totalno nedeterministican i ne daje mi puno sigurnosti.

Cijenio bih konkretne odgovore na postavljena pitanja, ako moze :slight_smile:

1 Like

Meni je ipak draza ona od gospodina Torvaldsa :smiley:

“Talk is cheap. Show me the code.”

― Linus Torvalds

1 Like
NoErrorInCode::noHelpProvided(true);

Pa briga i mene jel se procesor zagrijao…isto kao što me briga i za sve ostale side efekte koji neće izmjeniti ponašanje funkcije. :slight_smile:

Al za punjenje garbage kolektora me recimo nije moglo biti briga.
Zanimljivo kako se taj problem rješava.

Ako puno puta pozoveš npr. funkciju:

function calcDeltaCoodinate(){

	var delta_cor={x:0,y:0};
    //nešto se računa sa delta koordinatama ...
	delta_cor.x+= // ...računanje delta pomaka x
	delta_cor.y+= // ...računanje delta pomaka y

}

Koja samo unutar svog lokalnog scope-a kreira i računa delta koordinate …desit će se da zakucaš RAM memoriju. Zato jer svaki puta kada kreiraš objekt delta_cor, on ostaje negdje u radnoj memoriji i nakon što je funkcija završila svoj posao…tj. garbage kolektor ne uspijeva ponekad očistiti lokalno stvorene objekte.

I diljem interneta sugestirano rješenje da se nadiđe taj problem je:

var delta_cor = {x:0, y:0};
function calcDeltaCoodinate(item){

	delta_cor.x=0; delta_cor.y=0;
    //nešto se računa sa delta koordinatama ...
	delta_cor.x+= // ...računanje delta pomaka x
	delta_cor.y+= // ...računanje delta pomaka y

	
}

…tj. da se delta_cor drži kao globalni objekt, te da se njegovi atributi samo resetiraju svaki puta kod poziva funkcije calcDeltaCoodinate().
Na taj način neće kod svakog poziva funkcije biti kreiran novi objekt koji će negdje zaglaviti.

Da li je taj pristup ok?
Pa, ako rješava problem i nema pristupačnijeg rješenja…onda je valjda ok.
Ali treba u tom slučaju naravno paziti da neka druga funkcija ne koristi tu istu globalnu varijablu, jer će doći do neželjenog ponašanja programa.

I sad konačno pitanje, koji je tu side efekt?

Po tvojoj definiciji, ovaj drugi slučaj uzrokuje side efekt (jer dira globalnu varijablu) …dok prvi slučaj ne dira niti jednu globalnu varijablu pa ne može uzrokovati side efekt.

Ali u praksi, prvi slučaj će uzrokovati side efekt jer će napuniti RAM i posljedično utjecati na FPS igrice.
A drugi slučaj, ako se pametno postave globalne varijable za zaobilaženje problema zakucavanja RAM-a, neće uzrokovati nikakav side efekt na ostale funkcije …iako po tvojoj definiciji to jeste side efekt.

Mislim definicije su ok…ali ne treba ih se slijepo držati. Nego treba shvatiti zašto su postavljene kako su postavljene.
I ne treba zato slijepo slijediti definicije…nego se treba potruditi oko onoga zbog čega su iste nastale.
A to je u ovom slučaju da se ne naremeti mogućnost testiranja code-a zbog side efekta koji će promjeniti prijenosnu funkciju ulaza/izlaza neke funkcije.

Ne znam što bi ti?
Pa lijepo ti kažem da je jedna od mogućnosti da se zakačiš na spomenuti event, ako ga programski jezik omogućava. Naravno da nema svaki jezik ugrađene iste evente.
A ja ovdje ne pričam specijalno o niti jednom programskom jeziku, nego o metodologiji pristupa kako se može omogućiti da program djelomično testira sam sebe. A kako će se ta metodologija implementirati u određeni jezik, naravno ovisi o mogućnostima samog jezika.

A uz to sam ti dao više mogućih pristupa, gdje jedan od njih može biti primjenjen baš na svaki programski jezik.

Ja ponekad mislim da je @bozoou random word generator bot.

1 Like