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
mockDA = mock(DataAccess.class);
ps = new PriceService(mockDA);
}
@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
ps.getPrice("A100");
// 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: