Kihagyás

Tesztelés: CUnit keretrendszer

Tesztelni sokféleképpen és több szinten lehet. Tesztelési szintek például az egység, integrációs, és rendszerteszt szintek. Ezekből az "alap" az egységteszt. Ez a program (általában a megvalósításhoz köthető) kisebb egységeit (függvény, osztály, modul) egymástól függetlenül, önállóan teszteli. Ha már a program valamelyik önálló kis egysége önmagában hibás, akkor mit várunk az ilyen egységek együttműködésétől (integrációs teszt) vagy a teljes rendszertől (rendszerteszt)?

No de hogyan lehet a program egységeit, itt most tipikusan az egyes függvényeit önmagukban tesztelni? Ehhez egy olyan tesztprogram kell, ami sorban meghívja ezeket, és ellenőrzi a visszaadott eredményeket. Az ilyen tesztek implementációja egyrészt ugyanazon a programozási nyelven történik, mint amiben az adott egység meg van írva, másrészt valamilyen keretrendszert szokás hozzájuk használni. C nyelvre egy ilyen keretrendszer a CUnit. (Használatához linux alatt telepíteni kell a libcunit1-dev csomagot.)

A CUnit telepítése sudo/root jogokkal

Ha van sudo jogod egy linuxos gépen (tudsz root jogokkal futtatni programokat), akkor a CUnit telepítése rendkívül egyszerű:

1
$ sudo apt install libcunit1-dev

A CUnit telepítése sudo/root jog nélkül

Ha nincs megfelelő jogod, akkor magadnak kell telepítened a CUnit könyvtárat. Szerencsére ez könnyen megoldható, be kell szerezni a forrást innen, be kell konfigurálni, le kell fordítani, és telepíteni kell valami lokális könyvtárba. Ha letöltötted a CUnit-2.1-3.tar.bz2 fájlt, akkor a következőket kell tenni:

1
2
3
4
5
6
$ tar xf CUnit-2.1-3.tar.bz2
$ cd CUnit-2.1-3
$ ./bootstrap
$ make
$ make install
$ cd ..

​ Ezek után a $HOME/CUnitHome könyvtárban (vagy ha a ./bootstrap-nek adtál meg argumentumként egyet, akkor abban) megtalálod a telepített CUnit-ot. A fordításhoz szükséges header fájlok az include alkönyvtár alatt lesznek, a szerkesztéshez, futtatáshoz szükséges binárisok pedig a lib alkönyvtárban.

Példa (simpleTest.c):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <CUnit/Basic.h>
#include <CUnit/CUnit.h>

int add(int a, int b)
{
    return a + b;
}

void test_add(void)
{
    CU_ASSERT(add(2, 3) == 6); //helytelen művelet 
                               //(természetesen teszteknél sosem az elvárt eredményt igazítjuk a kívánt hatáshoz)
    CU_ASSERT(add(-1, 1) == 0); //helyes művelet
    CU_ASSERT(add(-1, -1) == -2); //helyes művelet
}

int main()
{
    CU_initialize_registry();
    CU_pSuite suite = CU_add_suite("AddTestSuite", 0, 0);
    CU_add_test(suite, "test of add()", test_add);
    CU_basic_run_tests();
    CU_cleanup_registry();
    return 0;
}

A teszteléshez a CUnit keretrendszert használjuk. Ehhez include-olni kell a <CUnit/CUnit.h> headert. A főprogram feladatai:

  • Inicializálni a tesztkörnyezetet a CU_initialize_registry() függvény segítségével.
  • Létre kell hozni a teszt suite-okat, amelyekhez hozzá kell adni a teszteket.
  • Futtatni a (regisztrált) teszteket a CU_basic_run_tests() segítségével. Futtatóból több is van, mi most azt választottuk, ami a standard outputra írja az eredményt. Ehhez szükséges a <CUnit/Basic.h> include-olása.
  • Végül "takarítani" a CU_cleanup_registry() függvénnyel.

Futtatás

A futtatáshoz a teszt programot (amely tartalmazza a tesztelés mainfüggvényét) kell lefordítani, összeszerkeszteni, és futtatni. Ezt természetesen megtehetjük statikus linkeléssel:

1
gcc -o test -I$HOME/CUnitHome/include -static -L$HOME/CUnitHome/lib simpleTest.c -lcunit

vagy akár dinamikussal is. Ez utóbbi esetben ne felejtsük beállítani futtatás előtt az LD_LIBRARY_PATH környezeti változót! (Érdekes lehet összehasonlítani, hogy a két esetben mennyire tér el a két bináris mérete!)

1
gcc -o test -I$HOME/CUnitHome/include -L$HOME/CUnitHome/lib simpleTest.c -lcunit

Futtatva a teszteket szép összefoglalást kapunk a sikeresen és sikertelenül lefutott tesztekről:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
     CUnit - A unit testing framework for C - Version 2.1-3
     http://cunit.sourceforge.net/


Suite AddTestSuite, Test test of add() had failures:
    1. simpleTest.c:11  - add(2, 3) == 6

Run Summary:    Type  Total    Ran Passed Failed Inactive
              suites      1      1    n/a      0        0
               tests      1      1      0      1        0
             asserts      3      3      2      1      n/a

Tippek

Egy ASSERT per teszt

Szokás azt mondani, hogy egy teszteset egyetlen ASSERT-et tartalmazzon, de ez néha nagyon elaprózhatja a teszteket. A lényeg, hogy egy teszteset logikailag egy dolgot teszteljen. Ahhoz például, hogy vajon két kép teljesen egyforma-e nyilván több ellenőrzés szükséges. Ezeket vagy külön-külön ellenőriztetjük egy-egy ASSERT segítségével, vagy mi magunk összegezzük, és a végén mondunk egy pass-t vagy FAIL-t.

Több teszt per unit

Egy függvényhez több tesztfüggvényt is lehet írni, ami ilyen-olyan adatokkal próbálja ki. Lehet például a határérték analízis alapján egy függvényhez több külön tesztesetet rendelni, hogy lássuk, melyik határértéknél van a hiba.

Tesztesetek nevei

Unit tesztek esetén a tesztek (tesztfüggvények) neveit a következőképpen szokás összerakni. Először a "test" szó, utána a tesztelendő egység neve, majd -- ha több tesztünk is van az egységhez -- a teszt funkciója.