12. diel - Ako testovať programy v C (knižnica assert.h) 2. časť
V minulej lekciám, Ako testovať programy v C (knižnica assert.h) , sme si Povedala o testovaní v céčku a vyskúšali sme si takzvaný Glass-box Testing. Dnes si povieme o test riadenom vývoji - (test-driven development - TDD).
TDD je technika tvorby softvéru, ktora bola navrhnutá s cieľom umožniť čo Najrýchlejšie zistenie a opravením chýb.Spravidla sa testy píšem este pred samotnou implementácie, ktora musí pred odovzdaním Všetky testy Prejsť. Vytvárané kód sa tu testuje už od zaciatku programovaním, nie až po dokončení celého modulu. Tento prístup by teda mal obmedziť neskorý odstraňovaním chýb na minimum, čím sa ušetríme vzácne zdroje - čas, pracovná sila ai Peniaze.
Tento Spôsob Testovanie sa nazýva ai Black-box Testing, pretoze modul (funkciám) je čierna skrinka a nevidíme do naj (niekedy este funkciám ani nie je napisane).
Parsing
Napadlo ma, že by sme si mohli Napísať pomocný kniznica na parsovanie vstupov od užívateľov.
Tak ideme na to, nie?
Založme si v IDE nový projekt s názvom Parsing, pridáme doňho
header Súbor parse.h a hlavný source Súbor (obsahuje funkciám main
()) premieňajú na parse.c.
Žiadny ďalší Súbor nebudú potrebovať, pretoze použijeme podmienený
preklad za pomoci funkcií preprocesoru.
Súbor parse.h bude vyzera takto:
#ifndef PARSING_H_INCLUDED #define PARSING_H_INCLUDED /** * Funkcia skontroluje, či vstupný reťazec obsahuje iba číslice, * ak nie, prvý odlišný znak zapíše na prvú pozíciu reťazca * @param vstupný reťazec (vstupno - výstupný parameter) * @return reťazec prevedený na int ak reťazec obsahuje iba číslice, inakšie 0 */ int parse_int(char *number); /** * Funkcia skontroluje, či reťazec obsahuje iba jednu desatinnú bodku, * ak ich obsahuje viac, vyhodnotí to ako chybu, * skontroluje, či obsahuje namiesto botky čiarku, ak áno, prepíše ju na bodku, * skontroluje, či vstupný reťazec obsahuje iba číslice (okrem jednej botky), * ak nie, prvý odlišný znak zapíše na prvú pozíciu reťazca * @param vstupný reťazec (vstupno - výstupný parameter) * @return reťazec prevedený na double ak reťazec obsahuje iba číslice, * alebo číslice a jednu bodku, inakšie 0 */ double parse_double(char *number); #endif // PARSING_H_INCLUDED
Súbor parse.c bude na zaciatky vyzera takto:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <assert.h> #include "parsing.h" #define TESTY // pri zakomentovaní budú testy odstavené int parse_int(char *number) { return atoi(number); // prevedie reťazec na int } #ifdef TESTY #define TESTY int main() { char *cislo1; cislo1 = (char*)malloc(10); strcpy(cislo1, "123456"); assert(parse_int(cislo1) == 123456); printf("Testy presli.\n"); free(cislo); } #endif // TESTY
Uložiť, skompilovat a spustiť.
Konzolová aplikácia
Testy presli.
=> Testy prešlo. A máme hotovo!
Ak chcete správne pochopiť Testovanie, bolo by dobré, keby ste Všetky kroky robili spolu so mnou.
Skus pridá ďalšie test:
cislo2 = (char*)malloc(10); strcpy(cislo2, "123456x"); assert(parse_int(cislo2) == 0);
výstup:
Konzolová aplikácia
Assertion failed: parse_int(cislo2) == 0, file C:\Users\libcosenior\Documents\cecko\Parsing\parsing.c, line 26
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
A máme Assertion failed. Prečo? Reťazec obsahuje iný znak Ako číslicu a
návratová hodnota má čo i 0 (nula).
Skus teda upraviť kód, napríklad takto:
if (atoi(number)) return atoi(number); else return 0;
Aaa zase chyba. Čo sa stalo? Zistíme to tak, že si vypíšeme reťazec prevedený na číslo, pridáme výpis do Funkcie main ():
char *cislo2 = "123456x"; printf("retazec\t%s\ncislo\t%d\n", cislo2, atoi(cislo2)); assert(parse_int(cislo2) == 0);
výstup:
Konzolová aplikácia
retazec 123456x
cislo 123456
Assertion failed: parse_int(cislo2) == 0, file C:\Users\libcosenior\Documents\cecko\Parsing\parsing.c, line 27
A hneď vidíme kde je problém. Funkciám atoi () vzala do úvahy číslice,
ktore bolí pred znakom x.
Ako to riešiť? Vyskusam zmeniť hodnotu * číslo2 na "123x456" a
otestujeme.
výstup:
Konzolová aplikácia
retazec 123x456
cislo 123
Assertion failed: parse_int(cislo2) == 0, file C:\Users\libcosenior\Documents\cecko\Parsing\parsing.c, line 27
Problém je tu stále. Funkciám atoi () prevedie číslice, ktore ŠÚ pred jedinou znakom na číslo, Preto teraz presunieme ten znak na zaciatok reťazca: char * číslo2 = "x123456";
výstup:
Konzolová aplikácia
retazec x123456
cislo 0
Testy presli.
Testy prešli a teraz už vieme, že ak je v reťazci iný znak Ako
číslice, treba ho vložiť na zaciatok reťazca.
Vyskúšajme upraviť funkciám takto:
for (int i = 0; i < strlen(number); i++) { if (!isdigit(number[i])) { // znak nie je číslica number[0] = number[i]; break; } } if (atoi(number)) { return atoi(number); } else return 0;
výstup:
Konzolová aplikácia
retazec 123x456
cislo 123
Testy presli.
Čo sa deje? Prečo je číslo 123 a nie 0, pričom testy prešlo? Všetko je v poriadku, ľan som zabudol zmazať (zakomentovať) Riadok s výpisom a ten neberie výstupy z funkciám.
Fajn. Zdá sa, že prvá funkciám je bezchybná. Napriek tomu by možno bolo ešte dobré to poriadne otestovať napríklad tak, že do textového subor napíšeme 100 rôznych reťazcov a tie sa buduje postupne testovať.
RGR systém
Videli sme, že funkció píšeme tak, aby prešla prvým testom skoro za každú cenu. Potom pridáte ďalšie test a tak Ďalej až do úspešného konca.
Tento systém sa nazýva RGR systém.
- RED - napíšte malý test, ktory nefunguje a na prvýkrát možné ani nepojde preložiť
- GREEN - rýchle spojazdnite test a neváhajte pri tom urobit hocijaka somariny
- Refactor - zbavte sa vsetkych duplicitu vytvorených v Snaha spojazdniť test
A tak stále dookola, kým nie je kód bezchybný.
Aby sme sa s tím Lepšie stotožnili, TDD sposobom si napíšeme ai druhý funkciám.
Pridáte si ju do subor parsing.c:
double parse_double(char *number) { }
a napíšeme prvý test.
/** testy funkcie int parse_double(char *number) **/ char *cislo3; cislo3 = (char*)malloc(10); strcpy(cislo3, "123.456"); assert(parse_double(cislo3) == 123.456);
Nedá sa to scompilovať, pretoze vo funkciám nemáme návratovú hodnotu. Doplníme.
return atof(number);
Skompilujeme a spustíme. Prešlo. Máme hotovo!
Samozrejme, že nie. Ideme najst Ďalšie Špeciálne zadanie od užívateľov.
strcpy(cislo3, "1.23.45.6"); assert(parse_double(cislo3) == 0);
Assertion failed! V reťazci Moze hoci okrem číslic maximálne jedna bodka (bodka). Čo s tým?
int point = 0; for (int i = 0; i < strlen(number); i++) { if (number[i] == '.') point++; } if (point > 1) return 0; else return atof(number);
Testy prešlo. Už zase máme hotovo. : -` Ďalším užívateľovho špecialitky:
strcpy(cislo3, "123,456"); assert(parse_double(cislo3) == 123.456);
V tomto prípade chceme, aby to vzalo ai tú Ciarka Namiesto botky.
Assertion failed!
Odstránime chybu tím, že doplníme do funkciám.
for (int i = 0; i < strlen(number); i++) { if (number[i] == ',') number[i] = '.'; }
Testy prešlo. Skus toto:
strcpy(cislo3, "123,45,6"); assert(parse_double(cislo3) == 0);
Assertion failed! Prečo? Vo funkciám treba najskôr ošetriť Ciarka a až potom botky. A bude to v poriadku.
Ďalšie zlé zadanie Moze hoci, že tam budem znaky mimo číslic, čiarok a bodiek.
strcpy(cislo3, "12x3.45,6"); assert(parse_double(cislo3) == 0);
Samozrejme, že je zase chyba. Vyriešime pridaním kódu:
for (int i = 0; i < strlen(number); i++) { if (!isdigit(number[i])) { number[0] = number[i]; break; } }
Testy prešlo.
Ale Skus ešte Niečo:
strcpy(cislo3, "12x3456"); assert(parse_double(cislo3) == 0);
Nebude tam Žiadna bodka, čo to spraví? Testy prešlo.
Fajn. Zdá sa, že ai druhá funkciám je bezchybná. Napriek tomu by možno bolo ešte dobré to poriadne otestovať napríklad tak, že do textového subor napíšeme 100 rôznych reťazcov a tie sa buduje postupne testovať.
Pozor dolezite!
Nikdy nezabudnite Nakoniec upraviť kód tak, aby bol čo Najjednoduchšia, aby
sa neopakovali Rovňák časti kódu a aby bol dobre okomentovaný. (Môj je
priložený.)
Teraz mozem Vypnutie Testovanie tak, že zakomentujeme Riadok:
//#define TESTY // pri zakomentovaní budú testy odstavené
a nepodarí sa nám to ani skompilovat, pretoze projektu CHYBA funkciám main
().
To nateraz nevadí. Niekedy, ked budete pulovr nebo vesta dver Funkcie
potrebovať, stačí do projekt pridať obidve subor a v subor, v ktorom sa
budem Funkcie používať, includovať parsing.h.
Takže Ako vidíte, Testovanie nás núti premýšľa KÉ všemožné vstupy sa mozu do Funkcie dosť a KÉ to mozem Mať následky na beh programu a núti nás to upraviť kód tak, aby sme Všetky problémy už dopredu vyriešili.
Samozrejme že, vývoj riadeni testami určite nie je univerzálnym liekom na všetko a dosiahnutie želaného efektu v ziadnom pripade nie je zaručené. Ale je to Najlepšia cesta, Ako sa maximálne vyhnutie možným problémom s nefunkčnosťou projektov konečného.
Ked sa nad tím zamyslím, vlastne vývoj všetkého sa testuje. Počnúc hračkami pre deti, cez napríklad Biela techniku, autá, zbrane až po rakety. Kto netestuje, vacsinou odovzdáva nekvalitne Posy a Preto Rýchlo skončí svoju kariéru.
Ja vám vsetkym držím palce pri Pisani svojich ložísk projektovej pomoci TDD.
Na záver prikladám zdrojové kódy napísané v Linux Ubuntu.
V budúcej lekciám, Funkcie s variabilným počtom a Typo argumentovať (stdarg.h) , si ukážeme Ako deklarovať funkciám s variabilným počtom a Typo argumentovať.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 113x (9.15 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C