• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Jak pisać testy własnego kodu

Object Storage Arubacloud
+2 głosów
458 wizyt
pytanie zadane 8 marca 2019 w C i C++ przez Mateusz Pawlak Użytkownik (960 p.)
Witam, programuje stosunkowo niedługo (3 miesiace) i zaczynam pisać coraz większe aplikacje. Obiło mi się o uszy (chyba nawet na jednym z filmów pasja informatyki) że można równolegle z kodem pisać testy sprawdzające poprawność działania poszczególnych funkcji. Chciałbym wprowadzić tą praktykę podczas pisania mało skomplikowanych programów (czyli teraz)  żeby wyrobić sobie dobry nawyk. Problem jest taki że nie mam pojęcia jak się za to zabrać  I tutaj zwracam się do społeczności forum o jakieś wskazówki, porady lub linki do sprawdzonych poradników o tej tematyce.

Z góry dziękuję za każdą formę pomocy

2 odpowiedzi

+3 głosów
odpowiedź 8 marca 2019 przez mokrowski Mędrzec (155,460 p.)
edycja 8 marca 2019 przez mokrowski

Poczytaj o TDD: https://en.wikipedia.org/wiki/Test-driven_development

Tu masz biblioteki które wspierają testy (oczywiście nie wszystkie): https://stackoverflow.com/questions/65820/unit-testing-c-code

Ja używam tego: https://github.com/catchorg/Catch2

oraz: https://github.com/google/googletest

Początek, etap RED. To oznacza na początku "że się kompiluje" i "po uruchomieniu testu nie przechodzi".

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

// Funkcja oblicza wartość kwadratu dla zakresu argumentów [10, 30].
// Przedział jest zamknięty czyli "włącznie z...".
int calculate(int val) {
    return 0;
}

int main(void) {
    assert(calculate(10) == 100);
    return EXIT_SUCCESS;
}

Kompilujesz, uruchamiasz i ...

$ ./calculate 
calculate: calculate.c:12: int main(void): Assertion `calculate(10) == 100' failed.
Przerwane (zrzut pamięci)

Wydzielam test (dalej jestem w RED), będzie porządniej. Brak w specyfikacji jak się zachować poza zakresem, coś przyjmuję i dokumentuję w teście.

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

// Funkcja oblicza wartość kwadratu dla zakresu argumentów [10, 30].
// Przedział jest zamknięty czyli "włącznie z...".
int calculate(int val) {
    return 0;
}

void test_range(void) {
    assert(100 == calculate(10));
    assert(900 == calculate(30));
    assert(121 == calculate(11));
    assert(841 == calculate(29));
    // Przyjmuję że w przypadku błędu, funkcja zwraca 0
    assert(0 == calculate(9));
    assert(0 == calculate(31));
}

int main(void) {
    test_range();

    return EXIT_SUCCESS;
}

Czas na implementację. Przechodzę do GREEN. Niektórzy (np. ja) wyróżniają tu etap GREY. To oznacza że kod "jakoś działa" ale wygląda słabo.. zasymuluję to.

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

// Funkcja oblicza wartość kwadratu dla zakresu argumentów [10, 30].
// Przedział jest zamknięty czyli "włącznie z...".
int calculate(int val) {
    if(val > 30) {
        return 0;
    }
    if(val < 10) {
        return 0;
    }
    return val * val;
}

void test_range(void) {
    assert(100 == calculate(10));
    assert(900 == calculate(30));
    assert(121 == calculate(11));
    assert(841 == calculate(29));
    // Przyjmuję że w przypadku błędu, funkcja zwraca 0
    assert(0 == calculate(9));
    assert(0 == calculate(31));
}

int main(void) {
    test_range();

    return EXIT_SUCCESS;
}

Na pierwszy rzut oka widać że warunek można połączyć. Drugie pytanie to co oznacza 0 zwracane z funkcji? Poprawiam TYLKO TO bo pisanie kodu TDD polega także na trzymaniu się etapów. Test jako zobowiązanie i stan GREEN jako rozwiązanie.

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

// Funkcja oblicza wartość kwadratu dla zakresu argumentów [10, 30].
// Przedział jest zamknięty czyli "włącznie z...".
int calculate(int val) {
    static const int OUT_OF_RANGE = 0;
    if((val > 30) || (val < 10)) {
        return OUT_OF_RANGE;
    }
    return val * val;
}

void test_range(void) {
    assert(100 == calculate(10));
    assert(900 == calculate(30));
    assert(121 == calculate(11));
    assert(841 == calculate(29));
    // Przyjmuję że w przypadku błędu, funkcja zwraca 0
    assert(0 == calculate(9));
    assert(0 == calculate(31));
}

int main(void) {
    test_range();

    return EXIT_SUCCESS;
}

Kod jest w stanie GREEN, może zostać wypchnięty do repozytorium.

Teraz następna pętla.... Hmm.. co przetestować? Przecież nie będę testował elementów (wszystkich) po kolei! Wybiorę 1, może 2... tak będę miał nadzieję że przetestowałem cały zakres.

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

// Funkcja oblicza wartość kwadratu dla zakresu argumentów [10, 30].
// Przedział jest zamknięty czyli "włącznie z...".
int calculate(int val) {
    static const int OUT_OF_RANGE = 0;
    if((val > 30) || (val < 10)) {
        return OUT_OF_RANGE;
    }
    return val * val;
}

void test_range(void) {
    assert(100 == calculate(10));
    assert(900 == calculate(30));
    assert(121 == calculate(11));
    assert(841 == calculate(29));
    // Przyjmuję że w przypadku błędu, funkcja zwraca 0
    assert(0 == calculate(9));
    assert(0 == calculate(31));
}

void test_value(void) {
    assert(484 == calculate(22));
    assert(225 == calculate(15));
}

int main(void) {
    test_range();
    test_value();

    return EXIT_SUCCESS;
}

O! Kod przeszedł przez RED i wskoczył do GREY! Czy można coś jeszcze poprawić by był czytelny? Np można tak:

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

// Funkcja oblicza wartość kwadratu dla zakresu argumentów [10, 30].
// Przedział jest zamknięty czyli "włącznie z...".
int calculate(int val) {
    static const int OUT_OF_RANGE = 0;
    static const int LOWER_BOUND = 10;
    static const int HIGH_BOUND = 30;
    if((val > HIGH_BOUND) || (val < LOWER_BOUND)) {
        return OUT_OF_RANGE;
    }
    return val * val;
}

void test_range(void) {
    assert(100 == calculate(10));
    assert(900 == calculate(30));
    assert(121 == calculate(11));
    assert(841 == calculate(29));
    // Przyjmuję że w przypadku błędu, funkcja zwraca 0
    assert(0 == calculate(9));
    assert(0 == calculate(31));
}

void test_value(void) {
    assert(484 == calculate(22));
    assert(225 == calculate(15));
}

int main(void) {
    test_range();
    test_value();

    return EXIT_SUCCESS;
}

Teraz możesz spokojnie się zastanawiać czy zmienić te stałe na makra, zmienić implementację mnożenia coś poprawiać. Kod masz broniony przez testy. Oczywiście należy je wydzielić do oddzielnych plików oraz tak naprawdę użyć biblioteki do testowania. Ja chciałem pokazać przykład więc użyłem prymitywnego assert(..)

0 głosów
odpowiedź 8 marca 2019 przez MiszczuBD Mądrala (5,320 p.)
Pytanie nie zbyt pełne, nie wiem jaki język i na jaką platformę piszesz, jednak dodajesz do kodu drugi kod który wywołuje po kolei funkcje tego pierwszego, jeśli wszystko działa to usuwasz drugi kod, kompilujesz i się cieszysz :P
komentarz 8 marca 2019 przez Mateusz Pawlak Użytkownik (960 p.)
Język to c++, a obecnie używam c++ builder. Ale w jakim celu je wywołuje i co świadczy o tym że wszystko jest ok? Porównuję wartości jakie zwraca funkcja z oczekiwanym czy jak?
komentarz 8 marca 2019 przez MiszczuBD Mądrala (5,320 p.)
Ten drugi kod wywołuje funkcje które chcesz sprawdzać... Typu

funkcja "abc" ma wywoływać print "bca"

i program ma użyć funkcji "abc" i zwrócić wynik czy została poprawnie wykonana.
Lepiej n ie wytłumaczę, poszukaj w google bo powinno być tego pełno

Podobne pytania

0 głosów
2 odpowiedzi 3,115 wizyt
pytanie zadane 28 października 2018 w Python przez Eliro Stary wyjadacz (12,160 p.)
+1 głos
1 odpowiedź 405 wizyt
pytanie zadane 28 lipca 2018 w PHP przez sapero Gaduła (4,100 p.)
0 głosów
2 odpowiedzi 846 wizyt
pytanie zadane 12 lipca 2018 w Java przez BuxBleed Użytkownik (810 p.)

92,570 zapytań

141,422 odpowiedzi

319,643 komentarzy

61,958 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...