Uvod

Već je široko prihvaćena činjenica da je softver trenutno glavni izvor inovacija u automobilskoj industriji. U prilog tome ide i činjenica da je od devedesetih, kada je automobil prosečno sadržao par MB binarnog koda, koji se izvršava na jednoj ECU računarskoj jedinici (Electronic Control Unit), do danas, količina softverskih artefakata po automobilu dosegla red veličine od nekoliko GB, i to isključujući mape i korisničke podatke,  a izvršava se na čak više od 150 ECU [1].

Sve veći broj funkcionalnosti, podrazumeva i porast broja zahteva, koje je potrebno dokumentovati iz više razloga, kao što su nadzor progresa projekta, pregled razvijenih funkcionalnosti, ali i testabilnost sistema. Prema tome, kako je softver u automobilu postajao sve kompleksniji, tako je i dokumentacija rasla, a u današnje vreme iznosi u proseku 100 000 stranica po automobilu [1], što je u proseku negde oko 3GB teksta (ilustrovano na Sl.1). 

Slika 1 Evolucija softvera u automobilskoj industriji

S obzirom na prirodu automobilske industrije, gde svaki propust može koštati puno – ne samo u pogledu materijalnih sredstava, već posledice mogu biti i fatalne po vozača, putnike i njihovu okolinu, od ključne je važnosti da implementirane softverske komponente ispunjavaju specifikaciju zahteva. Zbog toga, testiranje softvera je krucijalno u ovoj oblasti. Što se testiranja u automobilskoj industriji tiče, strategija je, generalno, da se kreće od nižih nivoa – pojedinačnih linija koda i funkcija, pa sve do celokupnog sistema. Proces testiranja softvera u automobilskoj industriji je prikazan na Sl. 2.

Slika 2 Proces testiranja softvera u automobilskoj industriji

U ovom članku, fokusiraćemo se na jedinično testiranje. Jedinični testovi su osnovni testovi nad individualnim entitetima softvera, poput klasa, modula i funkcija. Njihov cilj je otkrivanje defekata u implementaciji atomičnih jedinica – najčešće funkcija, odnosno metoda u okviru klasa. Za ovu svrhu se kreiraju slučajevi testiranja (test case) koji obuhvataju neku od metoda i odgovarajuće podatke koje ova metoda uzima kao ulaz. Nakon izvršenja testova, dobijeni rezultati se porede sa očekivanim vrednostima upotrebom različitih vrsta assert-a. Assert predstavlja tvrdnju u formi relacije između dobijenog i očekivanog rezultata – da li su jednaki, jedan veći ili manji od drugog, da li je dobijeni rezultat logičkog izraza true ili false, da li je dobijen null i slično. Ukoliko je ova relacija između dobijenog i očekivanog rezultata ispunjena, smatra se da je test uspešno prošao.

Međutim, kada implementiramo prototip ili pišemo testove, ponekad nije zgodno da se oslanjamo kompletno na realne objekte, zbog toga što dovodi do usložnjavanja i suvišnih zavisnosti u programu [2]. Iz tog razloga, još od najranijih dana jediničnog testiranja, javlja se pojam testiranja interakcije. Ovaj tip testiranja odnosi se na varijaciju jediničnih testova gde je glavni cilj utvrditi da li objekti korektno interaguju jedni sa drugim [3]. Za ovu svrhu koriste se mock klase, čiji objekti zapravo imitiraju realnu implementaciju, ali nisu potpuno funkcionalni. Implementacija ovakvih  klasa može biti prilično iscrpan posao, ali je od izuzetne važnosti u automobilskoj industriji s obzirom na veliki broj softverskih komponenti koje interaguju. U ovom članku, upravo je ovakav vid testiranja razmatran i to upotrebom specijalizovane biblioteke GMock [3, 4], koja ima za cilj da implementaciju mock klasa što i testiranje koje ih obuhvata što više olakša.

Što se framework-a za testiranje tiče, prikazaćemo upotrebu GMock-a unutar Google-ovog proizvoda GTest [4, 5], koji je namenjen jediničnom testiranju softverskih komponenti implementiranih u C++ programskom jeziku, na primerima iz automobilske industrije. Build-ovanje testove biće izvedeno uz pomoć Bazel alata.

Sa druge strane, uzimajući u obzir ogroman broj funkcionalnosti koje poseduju današnji automobili, očigledno je to da testiranje u automobilskoj industriji zahteva ogroman trud i vreme. Prema tome, u ovom radu bavićemo se i aspektom automatizacije generisanje testova za softverske komponente u automobilima primenom modelima-vođenog pristupa. Cilj je smanjiti vreme potrebno za kreiranje GTest+GMock testova primenom automatskog generatora koda na osnovu modela testa, koji će omogućiti što veće pokreivanje koda. Kao naš doprinos, predstavljamo i metamodel koji formuliše specifikaciju testova na visokom nivou. Korisnici u skladu sa ovom specifikacijom kreiraju model testa, a zatim generator koda na osnovu njega automatski generiše GTest kod. Za ovu svrhu, korišćen je Eclipse Modeling Framework (EMF).

GMock

GMock je biblioteka za kreiranje i upotrebu C++ mock klasa, koje imitiraju ostatak sistema u okviru jediničnih testova realizovanih upotrebom GTesta-a. Osnovni mehanizam se zasniva na upotrebi mock objekata koji služe za proveru interakcija između klasa koje oni menjaju i koda koji ih poziva. Mock objekti imaju identičan interfejs kao realni, pa ih mogu i zameniti, a daju i mogućnost da se definiše kako će se koristiti i šta će radit:

-Koje metode se pozivaju?

-U kom redosledu se vrše pozivi?

-Koliko puta se pozivaju?

-Sa kojim argumentima se vrše pozivi?

Mock klasa predstavlja “imitaciju” neke realne komponente softverskog sistema. Ona implementira prethodno definisanu apstraktnu klasu, istu onu koju i realna komponenta, imitirana od strane mock-a. Definicija mock klase se sastoji od navođenja mock metoda, koje služe da simuliraju prave pozive funkcija, a za svaku od njih povratni tip, naziv, argumenti i tip svakog od argumenata. Za svaku mock metodu koristimo makro MOCK_METHODN, pri čemu N predstavlja broj argumenata mock metode. Zatim, ovaj makro ima dva argumenta koji se odnose na poptis metode koju imitira: ime metode i tip povratne vrednosti sa argumentima i njihovim tipovima. Da bi se omogućio rad sa mock klasama, neophodno je na početku programa uključiti “gmock/gmock.h”. U Lisitngu 1 je prikazan šablon mock klase.

Listing 1 Šablon mock klase u GMock-u

U GMock-u, ključnu ulogu prilikom implementacije testova igra makro EXPECT_CALL kojim se definišu očekivanja prilikom poziva metoda mock klase. U ovom kontekstu, on obuhvata tri primarna aspekta - broj očekivanih poziva mock metoda, očekivani argumenti, ali i očekivane vrednosti koje metode vraćaju. U Tabeli 1 je dat pregled najčešće korišćenih opcija koje se koriste prilikom definisanja GTest test case-ova koji se oslanjaju na GMock.

Tabela 1 Najčešće korišćene opcije makroa EXPECT_CALL za očekivanja prilikom testiranja u GMock-u

Podrazumevano je da redosled izvršenja mock metoda nije relevantan. Međutim, ukoliko želimo uzeti u obzir i ovaj faktor, neophodno je u test case-u definisati InSequence objekat. Nakon toga, implicitno se podrazumeva da test ne prolazi ukoliko se redosled izvršenja ne poklapa sa redosledom navođenja mock metoda u okviru EXPECT_CALL makroa- test case-a.

Tek nakon definicije očekivanja uz pomoć EXPECT_CALL instanciramo ostale objekte i pozivamo njihove metode koje bi trebalo da se dalje služe funkcijama mock objekata. Posle toga, upoređuju se očekivanja sa ponašanjem koda koji se oslanja na mock objekte. Ukoliko se broj poziva, očekivane vrednosti, a opciono i redosled poziva poklapa sa očevkivanjima, onda je test prošao uspšeno. Za implementaciju testova koji koriste Gmock, osim “gmock/gmock.h“, potrebno je uključiti i “gtest/gtest.h“ na početku programa. Listing 2 prikazuje tipičnu strtukturu test case-a implementiranog uz pomoć GTesta, a oslanja se na GMock.

Listing 2 Šablon testcase-a kreiranog upotrebom sinergije GTest i GMock framework-a

Što se tiče build-ovanja Bazel projekata koji koriste GMock i GTest, neophodno je modifikovati WORKSPACE i BUILD fajl i to dovanjem unosa, što je prikazano na primeru u okviru Listing 3. Kao što se može videti, s obzirom da je GMock sada deo GTest-a (iako su inicijalno bili zasebni projekti) , dovoljno je dodati Git repozitorijum GTest-a u WORKSPACE, ali i gtest (odnosno gtest_main, ako naš test nema main funkciju) u okviru deps unutar BUILD fajla. Osim toga, u deps je potrebno dodati i sve klase koje se koriste u testovima – klase koje su predmet testiranja i čije se metode pozivaju, mock klase na koje se one oslanjaju, ali i apstraktne klase po čijem uzoru su im definisani interfejsi.

GMock testovi se pokreću na isti način kao i obični GTest testovi, komandom bazel test.

Listing 3 Bazel konfiguracija za GMock+GTest testove u programskom jeziku C++

Primer za GMock

U ovom primeru iz automobilske industrije prikazaćemo testove koji koriste dve mock klase - Engine i Vision,  a na njih se oslanja klasa za predstavljanje autonomnog automobila - Car, koja ujedno i predstavlja glavni predmet testiranja. Implementacija ove klase je data u okviru Listinga 4.

Listing 4 Imeplemtacija C++ klase Car koja se koristi u primerima za GMock (na putanji src/lib/car.h)

Metoda Start() služi za pokretanje automobila i to na sledeći način: pribavi se jedan frejm sa kamere i proveri da li je pristuna neka prepreka. Nakon toga,  pali se motor i vraća true.

Sa druge strane, metoda Go(int frames) za dati broj frejmova sa kamere, za svaki frejm utvrđuje broj prepreka i osoba koje se nalaze ispred automobila, a na kraju vraća true.

Dalje, što se mock klasa tiče: Engine – odnosi se na funkcionalnosti motora, poput paljenja – On(), gašenja – Off() i kretanja napred – Forward(); Vision – obuhvata funkcionalnosti računarskog vida, poput detekcije prepreka – FindObstacle i brojanja osoba - FindPeople. U Listingu 5 je data definicija ovih mock klasa. Pored mock klasa, neophodno je realizovati i ofgovarajuće apstraktne klase koje definišu njihov interfejs. Implementacija ovih klasa je trivijalna i nije prikazana u tekstu.

Listing 5 Imeplementacija mock klasa za GMock test klase Car (na putanjama src/lib/mock_engine.h i src/lib/mock_vision.h)

U nastavku su date implementacija dva testa Car klase upoterbom GMock. U Lisitngu 6 je dat kod prvog testa. Njegov cilj je jedinično testiranje metode Start() klase Car. Koriste se mock objeckti vision i engine, koji menjaju komponente odgovorne za računarski vid i rad sa motorom automobila na niskom nivou. U ovom slučaju, imamo dva očekivanja u pogledu interakcije klase Car i mock objekata: 1) bar jedan poziv metode vision modula za pronalaženje prepreka – FindObstacle 2) najviše jednom se pali motor – poziv metode On() koju implementira engine. Osim toga, očekuje se i poštovanje redosleda poziva – prvo se proveravaju prepreke, a zatim pali motor. Zbog toga je definisan i InSequence objekat.

Listing 6 GMock test interakcije metode Start() objekta klase Car

Sa druge strane, cilj drugog (prikazan u Listingu 7) testa jeste ispitati interakciju sa mock objektima prilikom poziva metode car.Go() za 3 frejma. U okviru makroa EXPECT_CALL definišemo očekivanje tačno 3 poziva metode FindObstacle, a takođe i 3 poziva metode CountPeople. Međutim, u ovom slučaju redosled nije relevantan za uspešnost testa, pa nema potrebe koristiti InSequence objekta.

Listing 7 GMock test interakcije metode Go() objekta klase Car

Što se BUILD fajla za testove tiče (prikazan u Listingu 8), neophodno je definisati cc_test build pravilo koje u okviru zavisnosti za sadrži sve mock klase, odgovarajuće apstraktne, ali i gtest ili gtest_main (ukoliko main metoda ne postoji u samom testu).

Listing 8 BUILD fajl projekta sa GMock+GTest primerima na putanji tests/BUILD

Autori: Maša Radenković, Nenad Petrović

Nastavak

Automatsko generisanje GMock testova: https://blog.syrmia.com/posts/ii-deo-automatsko-generisanje-gmock-testova

Osnove

[1] Uvod u Bazel, https://blog.syrmia.com/posts/uvod-u-bazel

[2] Uvod u GTest,  https://blog.syrmia.com/posts/gtest-sa-primerima

Dodatak

[1] N. Petrović, M. Radenković, S. Cvetković, D. Rančić: “Model-driven automated gMock test generation for automotive software industry”, XV International SAUM, pp. 1-4, 2021. https://www.researchgate.net/publication/353444345_Model-driven_automate...

[2] GitHub repozitorijum sa kodom: https://github.com/penenadpi/bazel_gmock_gtest_examples 

Literatura

[1] M. Staron, Automotive Software Architectures: An Introduction, Springer, Cham, 2017.

[2] What Is Google C++ Mocking Framework? [online]. Dostupno na: https://chromium.googlesource.com/external/github.com/google/googletest/+/refs/tags/release-1.8.0/googlemock/docs/ForDummies.md, poslednji pristup: 30/06/2021.

[3] J. Langer, Modern C++ Programming with Test-Driven Development, The Pragmatic Programmers, 2013.

[4] R. Osherove, The Art of Unit Testing with Examples in .NET, Manning Publications, 2009.

[5] A quick introduction to the Google C++ Testing Framework [online]. Dostupno na: https://developer.ibm.com/technologies/systems/articles/au-googletestingframework , poslednji pristup: 30/06/2021.

 

 

Add comment