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
- A JUnit mélyebb lelkivilágáról.
- Javadoc
- JUnit Best Practices a Javaworld szerint. Hasznos okosságok.
- Antipatterns az IBM-nél.
- Egy előadás shownote-jai. Nem túl hasznos.
- A JUnit recipes című papírkönyv, amiben sokminden benne van.
1 megjegyzés:
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
Megjegyzés küldése