Integrációs és ellenőrzési technikák (VIMIAC04)
Figyelem: A gyakorlatok során törekedjen mindenki arra, hogy professzionális jellegű munkát végezzen (pl. kerüljük az asdfg commit megjegyzéseket, nem sokkal több idő azt írni helyette, hogy Add acceleration feature vagy Fix #5).
A dinamikus ellenőrzési technikák legfőbb jellemzője, hogy a vizsgált forráskód a folyamat során végrehajtásra kerül. Ide tartozik a szoftvertesztelés is, amely a fejlesztési folyamat minden szintjén megjelenik. A legkorábbi szinten a fejlesztéssel párhuzamosan vagy közvetlenül azután történő tesztelési eljárás a unit tesztelés. Jelen gyakorlat a unit tesztelésre fókuszál.
A unit általánosságban a kód egy logikailag jól szeparálható része. Ez objektumorientált szoftverek esetén legtöbbször egy vagy néhány osztály együttesét jelenti. A unit legtöbb esetben egy jól definiált interfésszel rendelkezik, amelyen keresztül elérhető annak funkcionalitása. Ez egy kiemelten fontos aspektus egy szoftver tesztelhetősége szempontjából.
A unit tesztelés célja, hogy a fejlesztés során detektáljuk és javítsuk a felbukkanó hibákat (ez a legalacsonyabb a tesztelési szintek közül). A hibák korai, még a fejlesztés során történő felismerésével növelhető az elkészülő rendszer minősége és csökkenthetők a késői tesztelésből eredő többletköltségek. A unit tesztelése általában önállóan történik, izoláltan a többi egységtől. Ennek több előnye is van:
Egy darab unit teszt egy jól behatárolt funkcionalitást tesztel, gyakorlatilag egy viselkedési szerződést megadva a tesztelés alatt álló egység számára.
A gyakorlat során a már meglévő, továbbfejlesztett
Spaceship
projektet fogjuk használni. A cél a GT4500
osztály unit tesztelése több lépésen keresztül, iteratívan
felépítve.
A korábbi gyakorlatok során találkozhattunk azzal a problémával, hogy
a projekt tesztjei néha sikeresen lefutnak, ám néha sikertelenül futnak
(ez tipikus esete az előadáson látott "instabil teszt" nevű test
smell-nek). Ennek az az oka, hogy a TorpedoStore
osztálynak
a viselkedése nem-determinisztikus.
Így most a megbízható tesztelési környezet kialakításának első
lépéseként a GT4500
osztályt izoláljuk, így később már
annak a unit tesztelésére koncentrálhatunk. Tehát a
TorpedoStore
osztályt külső függőségként kezeljük. Ez a
unit tesztelés során azt implikálja, hogy az ilyen típusú objektum felé
történő hívásokat helyettesíteni, izolálni kell. A gyakorlaton a Mockito eszköz segítségével
valósítjuk ezt meg.
Az izolációt használva kétféleképp is tudunk ellenőrzéseket definiálni a tesztelés alatt álló unitunk felé. Egyrészt tudunk továbbra is állapotot vizsgálni azaz, hogy a teszt lefutása után milyen állapotba kerül a tesztelt objektum. A másik lehetőség, -- amelyre a mockok adnak lehetőséget -- hogy a tesztelt unitunk interakcióit ellenőrizzük (milyen hívásokat, milyen argumentumokkal intéz a környezete, függőségei felé).
A tesztelt unitunk izolációjának azonban számos akadálya lehet.
Köztük az egyik legismertebb probléma a tesztelhetőség. Ez a gyakorlaton
használt GT4500
osztályban is fennáll.
GT4500
osztályt úgy, hogy a
TorpedoStore
helyettesítő objektumai injektálhatóak
legyenek a tesztelt osztályba (ld. dependency
injection)!GT4500Test
tesztosztály
inicializáló logikáját (init
függvény) úgy, hogy a
GT4500
objektum létrehozása során a
TorpedoStore
-t helyettesítő mock objektumokat adjon át!
mock
nevű metódusának segítségével kell megvalósítani.GT4500
objektumot nem kell "mockolni", hisz
annak a valós implementációját akarjuk most tesztelni.TorpedoStore
valós implementációja
nem játszik szerepet a tesztekben, hanem a mock-okon beállított
viselkedés (when
hívások segítségével) fogja befolyásolni a
teszt eredményét.verify
).Alább látható egy egyszerű példakód egy másik projektből, hogy hogyan kell a Mockito metódusait használni. Ez szolgálhat mintaként, hogy milyen hívásokra lesz szükségünk a feladat megoldásához.
További információ a Mockito részletes dokumentációjában található.
public class PriceServiceTest {
private DataAccess mockDA;
private PriceService ps;
@Before public void init() {
// Create mock for the dependency DataAccess
= mock(DataAccess.class);
mockDA = new PriceService(mockDA);
ps }
@Test public void SuccessfulPriceQuery() {
// Arrange
// Set the behavior of the mock: if it is called with
// parameter "A100" then return the value 50.
when(mockDA.getProdPrice("A100")).thenReturn(50);
// Act
.getPrice("A100");
ps
// Assert
// Verifying the mock: getProdPrice was called only once
verify(mockDA, times(1)).getProdPrice("A100");
}
…}
Unit tesztek tervezésére általában három megközelítést alkalmaznak.
Tervezz meg egy szövegfájlban/táblázatban legalább 5 tesztesetet a
GT4500
osztály fireTorpedo
metódusához a
metódus specifikált viselkedése alapján. A tesztek tervezése során csak
a fejkommentben lévő szöveges leírást használd, magát a forráskódot
ne!
TorpedoStore
mock objektumokat a tesztesetekben lévő ellenőrzéseknél (ld. mock
példa)! Próbálj állapotokat és interakciót is ellenőrizni a mockok
segítségével (segítség itt)!Unit tesztelés során elengedhetetlen a folytonos visszacsatolás a tesztek által elért kódfedettségről. A fedettség mérésére minden programnyelvre léteznek megoldások. Java esetében a legismertebb eszközök: JaCoCo, Cobertura, Clover.
Jelen gyakorlat során a JaCoCo eszközt használjuk, amely egy Maven
plugin segítségével bárki által könnyedén telepíthető. A JaCoCo
működéséhez az alábbi kódrészleteket kell a pom.xml
fájlba
illeszteni.
dependencies
tagen belülre:dependency>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<scope>test</scope>
<dependency> </
build/plugins
tagen belülre:plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.7</version>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
<goals>
</execution>
</execution>
<id>default-report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
<goals>
</execution>
</executions>
</plugin> </
mvn verify
segítségével futtatva fog elindulni a fedettség
mérése a tesztfuttatások során./target/site/jacoco
mappába kell navigálni, és megnyitni az
index.html
fájlt. A megnyíló oldalon kattintsunk a
GT4500
osztály nevére, majd a fireTorpedo
metódusra. Így megnyílik az adott metódus színezett forráskódja. A
színek jelentése: