Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 11 - Testování v jazyce C

V minulej lekcii, Knihovny v jazyce C a C++, sme sa venovali tvorbe knižníc.

V dnešnom článku si povieme o testovaní programov (častí programov) v céčku.

Spoľahlivosť softvéru je miera jeho schopnosti vykonávať bezporuchovú činnosť. Spoľahlivosť softvéru sa však zväčša určuje až po dodaní produktu zákazníkovi a to na základe rôznych spätných väzieb. To je však príliš neskoro, pretože na opravu príčin porúch musia byť v tejto fáze vynaložené omnoho vyššie náklady ako na ich opravu už počas vývoja softvéru.

Na vyhodnotenie spoľahlivosti softvéru ešte pred dodaním zákazníkovi preto slúžia rôzne modely spoľahlivosti softvéru. Tieto modely sa však môžu značne líšiť vo svojich výstupoch. Každý model má určité predpoklady, ktoré musia byť splnené, aby bol výsledok modelovania čo najpresnejší. Pri výbere modelu sa musí brať do úvahy povaha testovania, charakteristika softvéru, ľudský zásah a spôsob odstraňovania chýb.

Najprv si prakticky ukážeme, prečo je vhodné všetko testovať. Napíšme si jednoduchý program, ktorý vyžiada od užívateľa kladné číslo a potom ho vypíše.

Program

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Zadajte kladne cislo a stlacte ENTER: ");
    int cislo;
    scanf("%d", &cislo);
    printf("Zadali ste kladne cislo: %d\n", cislo);
}

Aké ľahké. Lenže ako vieme, užívateľ je tvor nevyspytateľný a už len pre zábavu zadá napríklad -5.

Testing
Zadajte kladne cislo a stlacte ENTER: -5
Zadali ste kladne cislo: -5

Je jasné, že je to poriadna hlúposť. Predstavte si ale, že je to veľký projekt a ak nebude zadané kladné číslo, celý program sa zrúti...

Preto si tam doplníme jednoduchý test a predpokladám, že každý z vás to dokáže na základe doterajších znalostí napísať.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Zadajte kladne cislo a stlacte ENTER: ");
    int cislo;
    scanf("%d", &cislo);

    /** test **/
    if (cislo < 0) {
        printf("Niekto sa neucil matiku! Koniec programu!\n");
        return EXIT_FAILURE; // ukončenie programu
    }
    printf("Zadali ste kladne cislo: %d\n", cislo);
}

Vyskúšame.

Testing
Zadajte kladne cislo a stlacte ENTER: -5
Niekto sa neucil matiku! Koniec programu!

Assert

Dobre, máme to ošetrené, ale v názve článku je spomenutý header súbor assert.h, takže asi bude mať s testovaním niečo spoločné.

Pridajme si teda do nášho programu #include <assert.h>.

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

Táto knižnica obsahuje makro

void assert(int expression);

kde expression je výraz a môže to byť premenná alebo akýkoľvek céčkový výraz. Ak je výraz vyhodnotený ako TRUE, makro nerobí nič a program pokračuje ďalej. Ak je výraz vyhodnotený ako FALSE, zobrazí sa chybové hlásenie a preruší vykonávanie programu.

Tak si to ideme vyskúšať. Namiesto

if (cislo < 0) {
   printf("Niekto sa neucil matiku! Koniec programu!\n");
   return EXIT_FAILURE; // ukončenie programu
}

napíšeme

assert(cislo > 0);

Predpokladám, že vám je jasné, prečo sme dali výraz - číslo je väčšie ako nula. ;)

Po zadaní čísla 5 je výstup rovnaký ako v predchádzajúcom prípade, ale po zadaní čísla -5 bude výstup úplne odlišný.

Testing
Zadajte kladne cislo a stlacte ENTER: -5
Testing: /home/libco/cecko/Testing/glass_box_testing.c:12: main: Assertion 'cislo > 0' failed.
Aborted (core dumped)

Vypísala sa cesta k súboru, číslo riadku v ktorom je chyba a samotný chybový výraz.

Super. Zoznámili sme sa s makrom assert() a môžeme si povedať ako ho používať.

Testovanie

Ešte pred tým, ako si ukážeme skutočné použitie tzv. asercie, pripomenieme si typy chýb s ktorými sa v programoch stretávame.

V programe sa chyby ukazujú:

  1. behom prekladu. Tie "sa nám celkom páčia“, pretože nás na ich výskyt celkom presne upozorní prekladač a je otázkou chvíľky ich odstrániť.
  2. behom výpočtu vždy a okamžite. Tie "sa nám už páčia menej“, pretože ich výskyt musíme nájsť sami ladením a to vyžaduje trochu viac času.
  3. behom výpočtu po dlhej dobe alebo len niekedy. Tie "sa nám nepáčia vôbec“. Sú to najhoršie chyby. Vtedy program pracuje väčšinu času správne, ale občas, vplyvom (asi) špatnej konštalácie hviezd, chybne. Nájdenie tohto druhu chýb je časovo veľmi náročné.

Na to aby sa pri programovaní zabránilo maximálnemu počtu chýb, sa používa testovanie.

Najprv si povieme o testovaní nazvanom Glass-box Testing.

Glass box testing - Pokročilé konstrukce jazyka C

Prečo glass (sklo)? Pretože vidíme kód a na jeho základe vymýšľame testy, ktoré nám ho pomôžu doladiť k dokonalosti. :)

Ja som si v roku 2012 vypracovával úlohy z knižky od Herouta (aby som si dokázal, aký som šikovný) a napísal som napríklad takýto program:

/***********************************************
 * 12.c        ver. 1.0
 * Napíšte funkciu strings(char *s1, char *s2, int i);,
 * ktorá vloží do reťazca s1 od pozície i reťazec s2 a vráti ho.
 * Využite funkciu strcat(). Použite v programe.
 * ===========================================
 * libcosenior,     september 2012
 ***********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char* strings(char *s1, char *s2, int i)
{
    s1[i] = '\0'; // nastaví koniec prvého reťazca podľa zadanej pozície
    return strcat(s1, s2); // spojí prvý a druhý reťazec a vráti výsledok
}

int main(void)
{
    int pozicia;
    char *ret1, *ret2, cislo[3]; // deklarácie pointerov na reťazce, pole char na zadanie čísla

    if ((ret1 = (char *) malloc(80)) == NULL) { // dynamická alokácia s kontrolou voľnej pamäti
        printf("Malo pamati!");
        return 1; // ukončí beh programu - oznámi počítaču, že program skončil s chybou
    }
    if ((ret2 = (char *) malloc(80)) == NULL) {
        printf("Malo pamati!");
        return 1;
    }
    printf("Zadaj prvy retazec.\n");
    gets(ret1); // zadanie prvého reťazca
    printf("\nZadaj druhy retazec.\n");
    gets(ret2);
    printf("\nZadaj od ktorej pozicie prveho retazca sa ma vlozit druhy retazec.\n");
    gets(cislo); // zadanie reťazca číslo
    pozicia = atoi(cislo); // skonvertovanie reťazca na celé číslo
    printf("\n%s\n", strings(ret1, ret2, pozicia)); // výpis návratovej hodnoty volanej funkcie

    return 0; // ukončí beh programu - oznámi počítaču, že program skončil bez chyby
}

Program pracuje perfektne, pokiaľ mu zadávam správne hodnoty.

Testing
Zadaj prvy retazec.
SlonoNosoHroch je zvlastny tvor.

Zadaj druhy retazec.
Koza je domace zvieratko.

Zadaj od ktorej pozicie prveho retazca sa ma vlozit druhy retazec.
5

SlonoKoza je domace zvieratko.

V dobe jeho tvorby som vôbec nepremýšľal na nad tým, že užívateľ je bytosť absolútne nevyspytateľná a okamžite začne zadávať všetko možné, len nie to, čo by vyhovovalo programu.

Tak napríklad všetky zadania len od-Entruje a výsledok.

Testing
Zadaj prvy retazec.


Zadaj druhy retazec.


Zadaj od ktorej pozicie prveho retazca sa ma vlozit druhy retazec.

Program neurobí nič, pretože nie je užívateľu-vzdorný (blbu-vzdorný). :O

Tak a teraz nastupuje asercia. Includneme assert.h a vymyslíme, ako chyby detekovať. Napríklad použijeme výraz: veľkosť reťazca sa nerovná nule

gets(ret1);
assert(strlen(ret1) != 0);
printf("\nZadaj druhy retazec.\n");
gets(ret2);
assert(strlen(ret2) != 0);
printf("\nZadaj od ktorej pozicie prveho retazca sa ma vlozit druhy retazec.\n");
gets(cislo);
assert(strlen(cislo) != 0);
pozicia = atoi(cislo);

Skompilujeme, spustíme a zadáme Enter.

Testing
Zadaj prvy retazec.

Testing: /home/libco/cecko/Testing/glass_box_testing.c:35: main: Assertion 'strlen(ret1) != 0' failed.
Aborted (core dumped)

Vidíme, že asercia funguje a teraz opravíme kód tak, aby to chybu nevyhadzovalo.

while (1) {
    printf("Zadaj prvy retazec.\n");
    gets(ret1);
    if (strlen(ret1) != 0)
        break;
    else {
        printf("Zadal si len Enter!\nZa trest pockas 5 sekund, kym budes môct zadanie zopakovat!\n");
        sleep(5);
        system("clear");
    }
}
assert(strlen(ret1) != 0);
while (1) {
    printf("Zadaj pdruhy retazec.\n");
    gets(ret2);
    if (strlen(ret2) != 0)
        break;
    else {
        printf("Zadal si len Enter!\nZa trest pockas 5 sekund, kym budes môct zadanie zopakovat!\n");
        sleep(5);
        system("clear");
    }
}
assert(strlen(ret2) != 0);
while (1) {
    printf("\nZadaj od ktorej pozicie prveho retazca sa ma vlozit druhy retazec.\n");
    gets(cislo);
    if (strlen(cislo) != 0)
        break;
    else {
        printf("Zadal si len Enter!\nZa trest pockas 5 sekund, kym budes môct zadanie zopakovat!\n");
        sleep(5);
        system("clear");
    }
}
assert(strlen(cislo) != 0);

Skompilovať -> vyskúšať, ak je problém odstránený -> nájsť ďalší problém-> použiť aserciu -> upraviť kód a stále ďalej, až kým žiadne chyby nenájdeme.

Keď sa dopracujeme až sem, môže byť kód poriadne rozťahaný a neprehľadný, jeho časti sa môžu opakovať, program môže byť pomalý.

Preto urobíme ďalšie kroky:

  • odstránime opakovaný kód (použijeme funkcie alebo makrá)
  • popíšeme v komentároch všetky potrebné informácie
  • vypneme aserciu

Asercia sa vypína pomocou makra NDEBUG, ktoré sa zapne pred includovaním knižnice assert.h takto:

#define NDEBUG // pri zapnutí makra sa vypnú všetky príkazy assert()
#include <assert.h>

Môžete si povedať: Prečo príkazy assert() proste len nezmažeme? Preto že keď budeme v budúcnosti program upravovať, zase ich zapneme, aby sme mali pôvodné chyby ošetrené. Vypnutím asercie ju compilátor vôbec neberie do úvahy a preto sa program nespomaľuje dodatočným kódom.

Väčšina kompilérov asserty odstraňuje v produkčnej verzii kódu (napríklad so zapnutými optimalizáciami alebo v release verzii). Asserty teda slúžia k testovaniu programu pred samotným odovzdaním zákazníkovi, nie k validácii užívateľských vstupov. Vo väčšine prípadov nechceme, aby celý program spadol (to urobí assert), ale aby užívateľa upozornil na nesprávny vstup (alebo stav aplikácie) a dovolil mu chybu opraviť.

Nabudúce si povieme niečo o test-driven development (TDD) - testami riadenom vývoji.

Na záver prikladám céčkový súbor assert1a.c.zip, v ktorom som ošetril použitie súboru ako na platforme Linux, tak aj na platforme Windows za pomoci príkazov preprocesora. Spustiteľné súbory v archíve nie su, pretože aby mal súbor výpovednú hodnotu, treba si ho otvoriť v IDE a potom skompilovať na platforme, ktorú používate.

V budúcej lekcii, Testování v jazyce C pokračování, budeme pokračovať s testováním.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 105x (1.33 kB)
Aplikace je včetně zdrojových kódů v jazyce c

 

Předchozí článek
Knihovny v jazyce C a C++
Všechny články v sekci
Pokročilé konstrukce jazyka C
Přeskočit článek
(nedoporučujeme)
Testování v jazyce C pokračování
Článek pro vás napsal Libor Šimo (libcosenior)
Avatar
Uživatelské hodnocení:
12 hlasů
Obľúbil som si jazyk C a snažím sa ho spoznať čo najhlbšie. Začal som používať c# WPF, je to krása.
Aktivity