2007-12-02

JUnit 4

Haladni kell a korral (illetve nem szabad lemaradni), úgyhogy átnézegettem a JUnit 4 dolgait. Íme két hasznos olvasmány a témában -egyik az IBM-nél, másik DevX-en. A JUnit 4.X alapvetően a JUnit 3.X továbbgondolása olyan formában, ami a JDK5-ös új feature-jeit jól kihasználja. Jelenleg a 4.4-es verziónál tart, a www.junit.org -ról pedig letölthető (158kbyte a jar forrás nélkül). Mi változott?

  • Legalább 5-ös Java kell hozzá (bármilyen meglepő).
  • A package megváltozott junit.framework-ről org.junit-ra.
  • @Test annotációk használata névkonvenciók helyett. Továbbra is publicnak ill. void-nak kell lennie a visszatérési értékének és nem lehetnek paraméterei. Ha ezt nem tartjuk be, az alábbi kivételekre számíthatunk:
    java.lang.Exception: Method xxx should have no parameters
    java.lang.Exception: Method xxx should be void

  • Statikus import használata: import static org.junit.Assert.assertEquals; Így a kódban Assert.assertEquals(...); helyett írhatunk assertEquals(...);-t. (Persze JUnit 3.8-nál is használhatjuk a statikus import funkciót, ha 5-ös Javank van.)
  • Nem kell kiterjeszteni a TestCase osztályt, így lehetőve válik a protected metódusok tesztelése azáltal, hogy a teszt-osztállyal a tesztelendő osztályt terjesztjük ki.
  • setUp() metódus helyett a @Before annotációt használjuk, akár többet is. Kérdés hogy ezek milyen sorrendben hajtódnak végre - erre a sorrendre nincs explicit szabály, ezért véletlenszerűnek vehető.
  • A tearDown() párja pedig az @After, amiből szintén lehet több.
  • Ősosztályokban nem kell explicite meghívni a setUp()==@Before illetve tearDown()==@After metódusokat, mert azok hívása automatikusan történik: Először az ősosztályban lévő @Before-k hívódnak, aztán a leszármazott osztályokon belüliek. @After-nél pont fordítva, először a leszármazottaké, majd az ősosztályban lévők.
  • A @Before és @After minden teszteset előtt és után hívódik, akárcsak a setUp() és tearDown(). Lehetőség van @BeforeClass és @AfterClass metódusok megadására, amelyek az osztályban lévő összes teszteset előtt illetve után hívódnak meg. Ilyen feature JUnit 3.X-ben nincs.
  • Ha egy osztályban nincs egyetlen @Test annotáció sem, hibát fogunk kapni.
  • JUnit 3.X-ben a kivételek ellenőrzése úgy történt, hogy a catch blokkba beírtunk egy assert-et. Itt az annotációba írhatjuk be, hogy milyen kivételt várunk: @Test(expected=ArithmeticException.class). Ha nem dobódik kivétel, vagy más kivétel dobódik, a teszt elbukik. Ha további ellenőrzésekre van szükség a kivétel paramétereivel és szövegével kapcsolatban, továbbra is a try-catch módszert kell használni.
  • Ha valami tesztet mégsem akarunk futtatni, @Ignore annotációval kiüthetjük. (A @Test annotáció megmaradhat, elé és mögé is beírhatjuk az @Ignore-et.) Megadható neki egy String típusú paraméter, hogy miért hagyjuk ki. A teszt nem fog lefutni, viszont jelezve lesz hogy ki lett hagyva. Az egész osztályra is modhatunk @Ignore-t, de vigyázat, az nem ugyanaz mintha a metódusokra mondanánk, mert az utóbbi esetben még a @AfterClass és @BeforeClass lefut.
  • Általam nagyra értékelt feature, hogy a teszteseteknek timeout adható így: @Test(timeout=500) Milliszekundummal.
  • Van új assert, ami objektum tömböket hasonlít össze, viszont sok (12) assert metódus eltűnt az autoboxing feature miatt. Mindegyik az assertXXX(Object, Object)-et használja. Illetve a DevX szerint így történt, de én furcsa módon elérem ezeket a mindenféle paraméterű assert metódusokat továbbra is a TestCase osztályban.
  • Lehet használni az 1.4-ben bevezetett assert kulcsszót is, de akkor a tesztek futtatásánál meg kell adni az -ea kapcsolót a JVM-nek, különben az assert-ek nem kerülnek kiértékelésre. Ekkor viszont a JUnit-os assertException helyett a nyelvi java.lang.AssertionError-t fogjuk megkapni adott esetben.
  • JUnit4-ben nincs suite()
    metódus. Ehelyett lehet csinálni egy üres osztályt, aminek annotációban adjuk meg, hogy milyen más osztályokat futtasson:
    @RunWith(Suite.class)
    @Suite.SuiteClasses({My1Test.class, My2Test.class, My2Test.class})
    public class AllTests {
    }

  • @RunWith osztály annotáció megadásával saját futtatót is megadhatunk a tesztesetek futtatásához. Pl. a org.junit.runners.Parameterized-et, ami általunk megadott paraméterekkel küldi meg a tesztet. Egy @Parameters annotációval felturbózott publikus statikus metódusra van még szükség, ami egy Collection-t ad vissza és egy publikus konstruktorra, ami a Collection-ben lévő elemeket tudja fogadni. Pl. ha a Collection integer párokat tartalmaz, akkor a konstruktornak is két integer-t kell fogadnia. A futtató végigyalogol a Collection-ön, minden egyes esetben meghívja a konstruktort, majd végig a @Test-tel ellátott metódusokat. A DevX-es cikk harmadik oldalán van erről egy példa. (Listing 2.)
  • Ha ezt adjuk meg: @RunWith(TestClassRunner.class) ez nem okoz semmi különbséget ahhoz képest hogy nem adunk meg semmit, mivel a TestClassRunner a default runner.
  • JUnit4 nem különbözteti meg az előre várt hibákat a rosszul megírt tesztesetekből származő nem várt hibáktól -ez visszalépés! Ergó egy tesztesetnek a JUnit3.8 (passed/error/failure) eseteihez képest csak (passed/failure/(ignored)) esetei vannak.
  • java –ea org.junit.runner.JUnitCore paranccsal lehet futtatni a teszteket. Tud futtatni 3.8-as teszteket is a DevX cikk szerint, de a gyakorlatban 3.8-as tesztekben kell egy kis módosítás:
    public static junit.framework.Test suite() {
    return new JUnit4TestAdapter(MyTestClass.class);

    4.0-ás tesztek természetesen nem mennek 3.8-on. Ha régi és új JUnit is van a classpath-on, akkor pedig lehet hogy összebalhéznak. Nekem legalábbis securityException-nel leállt.
  • Hallottam már egy úgynevezett assertThat metódusról, elvileg van ilyen 4.4 óta, de még a junit.org-on aktuálisan fenn lévő javadoc-ban nem láttam.
  • Eclipse 3.3-ban van JUnit4.4 és JUnit3.8 támogatás.
  • Olyan szinten el*ja ez a szerkesztő a betűtípusokat és a formázást, hogy öröm nézni. Szörnyű. Update 2007.12.04: Megjavítottam direkt html szerkesztéssel.
Update 2008.11.20: JUnit3-ban szépen le lehetett kérdezni az éppen végrehajtott teszt nevét az örökölt getName() metódussal. Ez a lehetőség JUnit4-ben szépen elveszett. A mostani pillanatban nem tudok rá beépített megoldást, de ezt találtam a témában. Csúnya hosszú kód, nem próbáltam ki.

1 megjegyzés:

Kocka írta...

A junit 4 a junit 3 testng-sebbik verzioja :-) en csak azert maradtam meg a junit mellett, mert a testng ide supportja nagyon minimalis. De erdemes kiprobalni, van par agyas dolga, amit a junit meg nem tud.