Ivan Cvitaš
Autor:
Ivan Cvitaš

Programer za C#.NET programski jezik

Pisanje unit testova na legacy kodu

Što je legacy kod?

Legacy kod je bilo koji postojeći kod bez unit testova koji nitko ne razumije. Nije bitno koliko je star. Što je stariji manje je vjerojatno da smo upoznati s njim, čak iako je kod naš.

Što izbacivanjem legacy koda pokušavamo postići?

Mnogo je razloga zbog kojih treba promijeniti kod, ali obično su to kada trebamo:

  • Popraviti bug
  • Promijeniti ponašanje zbog promjene poslovnih pravila
  • Proširiti funkcionalnost kako bismo podržali nove značajke

Problem je što ne želimo napraviti nikakve greške, i ako nismo dobro upoznati s aplikacijom, vrlo je vjerojatno da ćemo nešto slomiti.

 

Unit testovi su naši prijatelji

Prije nego što promijenimo kod, važno je biti siguran da prvo razumijemo kako aplikacija funkcionira i znati jesmo li promijenili tu funkciju nakon što smo napravili izmjene.

Svrha unit testova:

  • Smanjuju bugove u novim i postojećim funkcionalnostima
  • Smanjuju troškove izmjena – manje bugova
  • Poboljšavaju dizajn projekta
  • Dozvoljavaju refaktoring
  • Prisiljavaju nas da malo usporimo i razmislimo tijekom razvoja
  • Smanjuju strah

 

Problemi s legacy kodom:

  • Nema testova – kod najvjerojatnije radi J
  • Teško refaktoriranje – hoće li kod još uvijek raditi?
  • Teško je proširiti funkcionalnost – potreba za izmjenom dijela starog koda
  • Kod zapravo posjeduje nas L        

Kako nam testovi vraćaju kontrolu nad kodom

               Refaktoring unaprjeđuje dizajn bez izmjene ponašanja koda. Testovi omogućavaju i osiguravaju da se ponašanje ne promijeni slučajno. Bez testova refaktoriranje legacy koda je zastrašujuće, pogotovo ako kod nije naš. Vremenom bez njega dizajn zastari i onemogućava nam unaprijeđene aplikacije.

Kako krenuti? Kako zapravo napraviti legacy kod održivim?

               Prvo moramo odrediti funkcionalnost koju želimo osigurati s testovima. Najbolje je krenuti sa malim stvarima, korak po korak. Nije cilj odmah pokriti što veći postotak koda, nego da se vremenom osiguraju najbitniji dijelove aplikacije. Dio na kojemu se stalno ponavljaju slični bugovi idealan je kandidat za prvi unit test.

               Nedavno na projektu susreo sam se sa zadatkom gdje je bilo vrijeme za napisati prvi unit test na dijelu aplikacije na kojemu su stalno dolazili novi bugovi. Riješi se jedan dođe sljedeći. Riješi se i taj, pa se vrati stari i još uz to pokoji novi… i tako u krug. S obzirom da je kod legacy i ljudi se mijenjaju na projektu teško je mijenjati logiku pogotovo ako kao u ovome slučaju nije ni logika ispravna 100%. Uz to dolaze novi zahtjevi za izmjenama dijela poslovne logike. Znači potrebno je nadograditi krivu logiku sa novim zahtjevom. Najveći problem je otkriti kako kod uopće radi, te je li nešto tamo sa razlogom ili je logička greška.

               Sada kada je cilj određen možemo početi. Kako bi se mogao uopće napisati unit test potrebno je prilagoditi kod za njega, napraviti ga testabilnim.

Kako?

  • Velike metode je potrebno izbjegavati i razbiti ih u više manjih. One zahtijevaju puno pripreme i puno scenarija kako bi se pokrili svi slučajevi. Kompliciraju sve.
  • Izbjegavati vanjske ovisnosti(dependencies) za koje je također potrebno puno pripreme i usporavaju nas. 

Prije:

Slika 1 Metoda u kojoj se računa koeficijent k1 (prije)

Slika 1 prikazuje metodu u kojoj je dio logike koju je potrebno izmijeniti za izračun koeficijenta k1. k1 je decimalni broj koji ima svaki ispust.

Problemi:

  •        Metoda nije odvojena u poseban logički sloj projekta, nego se nalazi odmah na viewu i pisanje unit testa nije moguće. Za unit test svakako se mora logika izdvojiti u ispravan sloj projekta.
  •        Nazivi pojedinih klasa su predugački i nerazumni. Nije nam odmah jasno što klasa zapravo predstavlja. Potrebno je izmijeniti nazive radi čitljivosti koda.
  •        Pojedine funkcije su nečitljive i nerazumne. Potrebno ih je napisati jasnije, odvojiti korake. Kada je potrebno izmijeniti takve funkcije napravi se više štete nego koristi.
  •        Izračun pokazatelja R nije na jednom mjestu. Izračunava se na početku metode i na kraju u foreach petlji, za određene uvjete postavlja se na null. Očito da je ovo bio brzi bugfix upravo zbog nerazumijevanja koda.

Proučavanjem izračuna vidi se da je za izračun korekcijskog koeficijenta k1 potrebna lista pokazatelja koji ulaze u izračun i pokazatelj R. R je nullable decimalni broj koji sudjeluje u formuli za izračun k1. k1 je potrebno izračunati za svaki ispust posebno. Početak metode grupira pokazatelje po ispustu, računa pokazatelj R i sprema ih u objekt rs. Rs je lista koeficijenata pokazatelja R za svaki ispust. Nakon toga ponovno se grupiraju pokazatelji, R se postavlja iz rs, a za korekcijskog koeficijenta k1 se poziva metoda DohvatiK1ZaIspust(slika 2). Prosljeđuje se lista analiza i pokazatelj R.

 

Slika 2 Metoda za računanje koeficijenta k1 (prije)

Metoda računa korekcijskog koeficijenta k1 po zadanoj formuli. Ovdje je situacija donekle jasnija nego kod pokazatelja R. Računa se prosjek pokazatelja iz analiza, pronalazi pokazatelj Bpk5 ukoliko postoji u listi i zajedno s pokazateljem R izračunava se k1. Ime metode treba sadržavati izračun, a ne dohvat k1 za ispust. 

Nakon refaktoringa, promjena naziva klasa, izdvajanje metoda na posebno mjesto, izračun R-a i k1 izgleda ovako i napokon su spremni za unit testiranje. Najteži dio posla je gotov. 

Poslije:

Slika 3 Metoda za izračun pokazatelja R (poslije)

Slika 4 Metoda za izračun korekcijskog koeficijenta k1 (poslije)

 

Kako napisati unit test?

Unit testovi moraju biti jednostavni, pokrivati što više slučajeve. Odlično bi bilo ubaciti i prave primjere. Obavezno je pokriti rubne slučajeve. Imena testova moraju biti jasna i bez uvida u test.

Primjeri unit testova za izračun korekcijskog koeficijenta k1:

Slika 5 Unit test 1 - primjer s nullable listom pokazatelja

Slika 6 Unit test 2 - primjer bez pokazatelja bpk5 

Nakon što su detaljno pokriveni svi slučajevi ovako izgleda lista uspješnosti testova. Testovi sadrže i prave primjere na kojima su se dogodile greške.

Slika 7 Lista uspješnosti testova

Namjerno je izmijenjen izračun pokazatelja R, te su testovi ponovno pokrenuti. Zakomentiran je uvjet za provjeru vrste prijemnika.

Slika 8 Metoda izračunaj pokazatelj R nakon izmjene

Ponovnim pokretanjem testova ne prolazi primjer gdje ne postoji pokazatelj s vrstom prijemnika sustav javne odvodnje. Očekuje se kao rezultat null vrijednost, ali to se nije dogodilo. Očekivano ponašanje, i pisanje unit testova u legacy kodu za izračun k1 je gotov. Osigurano je da budući zahtjevi za izmjenom izračuna ne stvaraju probleme. Metode su čitljive, lake za izmjenu i ako se kojim slučajem napravi greška na postojećoj logici ovi testovi će na vrijeme prikazati pogrešku i spriječiti veće probleme.

Slika 9 Lista uspješnosti testova nakon izmjene izračuna pokazatelja R

 

Isplati li se pisati unit testove u legacy kodu? Želimo li dobar ili loš kod?

Unit testiranje svodi se na pisanju dobrog koda koji se može testirati. Pisanje testova podiže kvalitetu i stanje aplikacije što na kraju dovodi do zadovoljnog korisnika. Testovima štitimo vlastiti rad i novim članovima tima biti će lakše nadograđivati i održavati sustav. Koliko puta ste samo poželjeli da znate što je netko prije mislio kada je pisao kod koji je sada vaš? J

Popularne teme
.NET ABAP ADFS Agile Always On Anemic Model Angular automatsko generiranje dokumnetacije Azure Backbone benchmark BI BI projekti Bootstrap building people business inteligence Business Intelligence Change Chrome CI CITCON Claims compile Continuous Delivery continuous deployment Continuous Integration CSR d3js data data visualization Data visualization alati DDD dekompozicija dependency injection dinamička forma dinamički parametri dinamički query distribuirani razvoj dokumentacija Domain-Driven design DOP društvena odgovornost edge-based video analytics Eliminating waste enkapsulacija enterprise razvoj softvera ERP ETL Excel FIORI Frontend funkionalna dokumentacija game Geopackage GPKG GIS Git Groovy heat map HICCUPS Hichert HTML IBCS interoperability invision IoT IPSO izvještavanje java JavaFX Javascript Jazz Build Engine JBE Jenkins jquery jqueryui jsfiddle JVM Kaizen Kanban king KING ICT Kingovci Knockout kvaliteta lambde leadership Lean legacy code M language Management Maven Metodologija microservices Microsoft mobile Mobility mockups moć monday game NetWeaver network nodejs oblikovni obrasci OGC OKR open source optimizacija organizacija organizacijska struktura OutOfMemoryError outsourcing overengineering paginacija Performance performanse PERT PMI PMP; Agile; Project management; Scrum; KING ICT; razvoj; metodologija podatkovni skup pouzdanost Power BI Power Map Power Pivot Power Query Power View pretraga proces procjena Product Owner programming proizvod Project manager projektni plan radar Rational Team Concert razvoj tima refaktoriranje Release resize responsive charts REST retrospektiva Rich-Domain model Roko Roić rolling wave planning RTC SAP scale scatterplot chart Scrum scrum team scrum tim service boundaries single responsibility principle Single Sign-On smart metering SoapUI social responsibility softver Software software prototyping Software Testing Club Spring Boot SQL standard sustav videonadzora svg swagger tdd Team team building team development Team Foundation Server tech tehnologije terminski plan Testing tim timesheet timovi Toggl.com touch transakcijski nadzor tražilica underengineering unit testing Uspjeh Visual Studio vodstvo vodstvo leadership moć društvena odgovornost DOP social responsibility CSR vođenje projekata WBS Web Zagreb STC

PRIJAVA NA NEWSLETTER

Najnovije novosti iz ICT svijeta