Kihagyás

White Box Testing

Lefedettségek

White box lefedettségek esetében azt vizsgáljuk, hogy a szoftver kódját milyen mértékben fedik le/futtatják a tesztek.

Utasítás tesztelés (utasítás, eljárás szinteken)

Utasítás tesztelés esetén azt vizsgáljuk, hogy a program egyes egységei végre lettek-e hajtva a tesztek futtatása során, vagy sem. Az utasítás ebben az esetben jellemzően tényleges gépi vagy forráskód szintű utasítást illetve forráskód-sort jelent, de basic-block, esetleg függvény/metódus, vagy egyéb elem is lehet. A lényeg, hogy a szoftver egy jól meghatározott darabját érintjük-e, vagy sem? Pontosabban, az öszes ilyen darab mekkora részét érintettük?

Elágazás tesztelés (utasítás, eljárás szinteken)

Elágazás lefedettségek esetén azt vizsgáljuk, hogy a program különböző döntési pontjait a tesztek mennyire alaposan dolgoztatják meg. Vagyis azokon a pontokon, ahol igaz/hamis alapon kétféle döntés születhet (és ez kétféle lefutást eredményez), a tesztek során előállnak-e ezek a lehetőségek? Az egyes elágazásokat egymástól függetlenül vizsgáljuk.

  • Döntési lefedettség: A programban található elágazás mindkét ágára ráfutottunk-e a tesztek során? Pontosabban, a lehetséges ágak mekkora részére futottunk rá? Döntési lefedettséget lehet alacsony (gépi kód) vagy magasabb (forráskód) szinten is mérni. Az egyszerű döntéseknél nagyjából egyértelmű a helyzet, a ciklusoknál viszont nem elég, ha a ciklusfeltételt mint egyszerű döntést vizsgáljuk. Ciklusoknál a két lehetséges döntés "az első adandó alkalommal elhagyom a ciklust" és a "legalább egyszer megismétlem a ciklusmagot". Ez egy döntésre vetítve 2 tesztesettel megoldható, összesen pedig általában döntési szintenként (a döntések egymásba ágyazottsági mélysége szerint) 2 tesztet jelent, ha az egy szinten lévő döntések egymástól függetlenek.

  • Döntési feltétel lefedettség: A program egy-egy döntése több feltételből állhat. A feltétel lefedettséggel azt vizsgálnánk, hogy az egyes feltételek igaz/hamis kimenetei közül mennyi került elő a tesztek során. A kimenetek teljes (egymástól független) lefedése viszont még nem garantálná, hogy magának a döntésnek is mindkét kimenetét ellenőrizzük, így a feltétel lefedettséget önmagában nem, csak a döntési lefedettséggel kombinálva szoktuk használni. (Ha például egy A vagy B döntésünk van A és B feltételekkel, akkor az (A,B) = {(i,h), (h,i)} kombinációk 100%-os feltétel lefedettséget adnak, hiszen A és B is felveszi mind az i (igaz), mind a h (hamis) értékeket, de a döntés mindkét esetben igaz lesz.) Ez egy döntésre vetítve általában 2 tesztesettel megoldható.

  • MCDC lefedettség: A Modified Condition Decision Coverage esetében minden egyes feltételhez úgy kell két tesztet rendelni, hogy a döntés többi feltétele a két tesztben ugyanolyan értéket kapjon, viszont az adott feltétel értéke és ezáltal a döntés végeredménye is a két tesztben különbözzön. Vagyis azt nézzük, hogy ha csak az adott feltétel változik meg egy döntésben, akkor az a döntés végeredményét is meg kell, hogy változtassa. Ehhez egy n feltételt tartalmazó döntés esetében legalább n+1, legfeljebb 2n tesztesetet jelent.

  • Teljes lefedettség: Minden döntésben a feltételek összes lehetséges kombinációját le kell fednünk. Egy n feltételt tartalmazó döntés esetén (ha a feltételek függetlenek, és teljes kiértékelést csinálunk) ez 2n tesztesetet jelent.

  • LCSAJ lefedettség: Linear Code Sequence And Jump: lefedjük-e a tesztekkel az összes olyan lehetséges kódsorozatot, aminek a végén egy ugrás van?

  • Hívási lefedettség: Eljárás szinten, minden lehetséges f()-ből g()-be irányuló hívás megvalósult-e a tesztek során.

Útvonal tesztelés

Útvonal tesztelés esetén azt vizsgáljuk, hogy a programban végrehajtható lehetséges útvonalak mekkora részén mentünk végig a tesztelés során.

  • Komplexitás alapú (McCabe): A McCabe komplexitás azt mondja meg, hogy a lehetséges végrehajtási utak közül mennyi "lineárisan független" van. Elvileg ezek letesztelésével a program "elég jól" le lesz tesztelve.

  • Baseline módszer: Válasszunk egy tesztesetet/útvonalat, és hajtsuk végre. Ezután vegyük az útvonal utolsó elágazását, és ha ennek a másik ágát még nem teszteltük, akkor tegyük meg. Ha az utolsó elágazás mindkét ága tesztelve volt, akkor lépjünk vissza az előző elágazáshoz, és ezt vizsgáljuk.

  • Ciklikus módszer: Ha van egy ciklusunk, akkor azzal végtelen számú útvonalat tudunk generálni. Hogy ezt elkerüljük, egy-egy ciklusnál kétféleképpen tesztelünk: az egyik, amikor kihagyjuk a ciklust, a másik, amikor legalább egyszer megismételjük.

  • Adatfolyam tesztelés: Változók definíciói és felhasználási helyei (c-use: computational; p-use: predicate) közötti lehetséges útvonalak tesztelése.

    • Stratégiák egy v változóra:
      • Minden def-USE út (def(v,m), USE(v,n): m⟶n)
      • Minden use: minden def-USE párhoz legalább egy út
      • Minden c-use, néhány p-use / Minden p-use, néhány c-use: minden def⟶x-use -hoz van út, vagy legalább egy def⟶^x-use út van a def-hez
      • Minden c-use / Minden p-use: csak a megfelelő use-zal rendelkező változókat használjuk
      • Minden def, bármilyen use

Mutációs tesztelés

 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
26
27
28
29
30
31
32
33
int count(int **matrix, int nx, int ny, int dx, int dy) {
    int i = 0, j = 0;
    int mx = 1, my = 1;
    char dir = 'f';
    while (i < ny * nx) {
        if (0 <= x && x < nx && 0 <= y && y < ny) {
            if (i) {
                fputc(',', out);
            }
            fprintf(out, "%d", matrix[y][x]);
            ++i;
        }
        ++j;
        if (dir == 'f') {
            y += dy;
            if (j == my) {
                ++my;
                dy = -dy;
                dir = 'v';
                j = 0;
            }
        } else {
            x += dx;
            if (j == mx) {
                ++mx;
                dx = -dx;
                dir = 'f';
                j = 0;
            }
        }
    }
    return mx * my;
}
 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int main() {
    int max1, max2, max3, elem;
    scanf("%d", &elem);
    if (elem == 0) {
        printf("A 0 elemű sorozatnak nincs legnagyobb eleme.\n");
        return 0;
    }
    max1 = max2 = max3 = elem;
    scanf("%d", &elem);
    if (elem == 0) {
        printf("A sorozat legnagyobb eleme: %d.\n", max1);
        return 0;
    }
    if (elem > max1) {
        max1 = elem;
    } else {
        max2 = max3 = elem;
    }
    scanf("%d", &elem);
    if (elem == 0) {
        printf("A sorozat legnagyobb elemei: %d, %d.\n", max1, max2);
        return 0;
    }
    max3 = elem;
    do {
        if (elem > max1) {
            max3 = max2;
            max2 = max1;
            max1 = elem;
        } else if (elem > max2) {
            max3 = max2;
            max2 = elem;
        } else if (elem > max3) {
            max3 = elem;
        }
        scanf("%d", &elem);
    } while (elem != 0);
    printf("A sorozat legnagyobb elemei: %d, %d, %d.\n", max1, max2, max3);
    return 0;
}

Utolsó frissítés: 2023-03-21 09:03:13