2008-02-04

JUnit 3.8 Intermed

Azt gondolná az ember hogy tesztesetek írása csupa unalmas ujjgyakorlat.
Itt most ismét JUnit 3.8-as dolgokról lesz szó + általános elvekről.

Failures vs Errors


Alapvetés: A failure és az error is elbuktatja a tesztesetet. A failure-re számítunk, az error-ra nem.

A failure AssertionFailedError-t eredményez. Hogyha a teszt amiatt bukik el, hogy a setUp()-ban vagy a tearDown()-ben exception keletkezett, az pl. error-t eredményez.

Figyelem: A fail()-t és az elbukott assert-eket elkapja a catch(Error), mivel ezek AssertionFailedError-t dobnak. Úgyhogy ha valami (különös) oknál fogva Error-t kell elkapnunk vagy ellenőriznünk, az AssertionFailedError-t tovább kell dobnunk, mert különben szépen csendben elnyelődik. (Pl. külön catch ágat adhatunk az AssertionFailedError-nak.)

A teszt metódusokat el lehet látni throws kitétellel. Ha bekövetkezik az exception, Failure lesz a teszt eredménye. Az a mondás, hogy jobb throws kitételt írni a teszt metódushoz, mint a metóduson belül catch-sel elkapni és kézzel fail-t hívni. "Sosem kapjunk el nem várt exception-öket."

Text test runner kimenetei:
. pass, F:failure, E:error

Assertions


Az asserteknél általában mindig az első paraméter a várt, a második a kapott érték. Ha message is tartozik az assert-hez, az ezeket mind megelőzi.

assertNotNull: van ilyen, nem kell assertEquals(true, obj==null)-t használni.

Az assertSame szolgál a referenciaegyenlőség vizsgálatára.

Az assertEquals nem használható tömbök egyezőségének vizsgálatára. Tömbök egyezőségének vizsgálatához ez ajánlott:
assertTrue(java.util.Arrays.equals(expected, actual));

Redundant assertion antipattern: pl. assertTrue(true)-t betenni olyan helyekre (több helyre), ahova szerintünk elér a vezérlés.

Egyebek


junit.extensions package: A test decorator-ok, mint például a RepeatedTest arra valók, hogy különféle kiegészítő viselkedéseket vezessünk be a tesztek futása előtt illetve után. A test decoratorok tehát nem test suite-ok.

Nem lehet párhuzamosan (szimultán) teszt metódusokat futtatni egy TestCase-en belül. Az ActiveTestSuite-tal lehetőség van tesztek futtatására oly módon, hogy minden hozzáadott TestCase külön szálban indul el párhuzamosan, de a TestCase-eken belül a teszt metódusok továbbra is egymás után hajtódnak végre valamilyen sorrendben.

Interfészekkel tesztelni egyszerűbb. Emiatt (extrém megközelítés) a tesztelés szempontjából elónyösebb, ha a tesztelendő komponensben minden interfész. (A lokális változók, a visszatérési értékek és a függvény paraméterek.)

Hogyan győződhetünk meg róla, hogy a tesztjeink függetlenek egymástól:
-Ugyanazokat a teszteket futtassuk kétszer egymás után.
-Futtassuk a teszteket különböző sorrendben.
-Néhány tesztet üssünk ki mesterségesen és győződjünk meg róla, hogy a többi teszt továbbra is rendben lefut.

A TestSetup osztállyal lehetőség van a normál setUp és tearDown működést kiegészíteni pl. így:


public class TestBigClass extends TestCase {
public void testMethod1() {
System.out.print("A");
}

public void testMethod2() {
System.out.print("B");
}

public static void customSetUp() {
System.out.print("C");
}

public static void customTearDown() {
System.out.print("D");
}

public void setUp() {
System.out.print("E");
}

public void tearDown() {
System.out.print("F");
}

public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTestSuite(TestBigClass.class);

TestSetup setup = new TestSetup(suite) {
public void setUp() {
customSetUp();
}

public void tearDown() {
customTearDown();
}
};
return setup;
}
}


A futtatás eredménye: CEAFEBFD vagy CEBFEAFD, mivel A és B sorrendje elvileg véletlenszerű, tehát a custom setup és teardown közrefogja a teszteket a normál setup és teardown-okkal együtt.

A JUnit a lelke mélyén nagyjából a következő template metódust használja a tesztek futtatásához:

public void runBare() throws Throwable {
setUp();
try {
runTest();
} finally {
tearDown();
}
}

Fontos megállapítás, hogy mindent dobhat (throws Throwable) és a tearDown() mindenképpen lefut.

Minták


Mock object: stub (static return)- fake (not yet implemented) - mock (valamit csinál) Bővebben.
Self-shunt: A tesztosztály egy callback jellegű interfészt implementál, ahol magukba a callback metódusokba írjuk az assert-eket.
Crash test dummy: Hibakezelés tesztelésére. Szándékosan hibát idézünk elő. Elkapjuk a várt kivételt és esetleg megvizsgáljuk a paramétereit vagy a környező változókat.
AbstractTestCase: Az absztrakt teszt eseteket is a TestCase osztályból örököltetjük, csak teszünk bele absztrakt metódusokat, például különféle factory metódusokat amelyek a tesztekhez szükséges objektumokat gyártják le. Az absztrakt tesztosztályban csak a közös vonásokat teszteljük le.
Extract repetitive setup code to fixture: Ne ismételjünk fölöslegesen a teszt metódusokon belül. Emeljük ki az ismétlődő dolgokat a fixture-ökbe: setUp(), tearDown().
Collecting Parameter Pattern: Amikor több tesztmetóduson keresztül kell összegyűjteni az eredményeket, akkor a metódusnak átadhatunk egy paramétert, amibe az eredményt belepakolhatja. A junit.framework.TestResult egy olyan osztály, amit erre felhasználhatunk. Tárolja hogy mennyi teszt futott összesen, tárolja az error-ral és failure-rel elbukott tesztek referenciáit.
Parameterized test case: Egy előző postban már írtam ilyesmiről, JUnit4 témában. Nem tudom 3.8-ban hogyan működik.
Base test case: Erről nem találtam semmit, de biztos köze van az öröklődéshez.
Good test naming: Nevezd el ésszel a teszteseteidet.

Linkek


1 megjegyzés:

Schmidt Roland írta...

Helló pcjuzer,

teszt programozót keresek automatizálási tapasztalattal égen-földön, de nem találok. Tudsz segíteni? Ajánlani vkit?

Schmidt Roland
+36 20 953 0870
sroland@schmidtinfo.hu