2013-06-06 15 views
14

Jestem programistą C++ i jeśli chodzi o testowanie, łatwo jest przetestować klasę przez wstrzykiwanie zależności, nadpisywanie funkcji członków itd., Aby można było łatwo testować przypadki brzegowe. Jednak w C nie można używać tych wspaniałych funkcji. Ciężko mi dodać testy jednostkowe do kodu ze względu na niektóre "standardowe" sposoby pisania kodu C. Jakie są najlepsze sposoby, aby zmierzyć się z następujących czynności:Testy jednostek pisania dla kodu C

Przechodząc wokół dużego „kontekście” struct palików:

void some_func(global_context_t *ctx, ....) 
{ 
    /* lots of code, depending on the state of context */ 
} 

łatwy sposób testować awarię na funkcjach zależnych:

void some_func(....) 
{ 
    if (!get_network_state() && !some_other_func()) { 
    do_something_func(); 
    .... 
    } 
    ... 
} 

Funkcje z wieloma parametrami:

void some_func(global_context_t *, int i, int j, other_struct_t *t, out_param_t **out, ...) 
{ 
    /* hundreds and hundreds of lines of code */ 
} 

statyczne lub ukryte funkcje:

static void foo(...) 
{ 
    /* some code */ 
} 

void some_public_func(... } 
{ 
    /* call static functions */ 
    foo(...); 
} 
+0

[Możliwe duplikaty] (http://stackoverflow.com/questions/65820/unit-testing-c-code), jeśli nie tylko powiązane. – ajp15243

+0

@ ajp15243: Mniej mnie interesuje struktura lub narzędzie używane podczas testów jednostkowych. Raczej interesują mnie sposoby pisania testu na kod C, który jest wysoce zależny lub używa wspólnych idiomów C, takich jak struktura 'global_context_t', która jest przekazywana. – MarkP

+0

Cóż, mógłbym edytować "duplikat", gdybym mógł w tym momencie. Pomyślałem, że możesz przynajmniej znaleźć frameworki pomocne, ale no cóż: /. – ajp15243

Odpowiedz

8

Ogólnie, zgadzam się z odpowiedzią Wesa - znacznie trudniej będzie dodać testy kodu, który nie jest napisany z myślą o testach. Nie ma nic nieodłącznego w C, co uniemożliwia sprawdzenie - ale ponieważ C nie zmusza cię do pisania w konkretnym stylu, bardzo łatwo jest napisać kod C, który jest trudny do przetestowania.

Moim zdaniem, pisanie kodu z myślą o testach zachęci krótsze funkcje, z kilkoma argumentami, które pomogą złagodzić ból w przykładach.

Najpierw należy wybrać ramy testowania jednostkowego. Istnieje wiele przykładów w this question (choć niestety wiele z nich to framework C++ - odradzam używanie C++ do testowania C).

Ja osobiście używam TestDept, ponieważ jest łatwy w użyciu, lekki i umożliwia krępowanie. Jednak nie sądzę, by był on jeszcze bardzo szeroko wykorzystywany. Jeśli szukasz bardziej popularnej architektury, wiele osób poleca Check - co jest wspaniałe, jeśli używasz automake.

Oto kilka konkretnych odpowiedzi dla swoich przypadków użycia:

Uboczny wokół dużego „kontekście” struct wskaźnik

W tym przypadku można zbudować instancję struct z warunki wstępne zostały ustawione ręcznie, a następnie sprawdź status struktury po uruchomieniu funkcji. Dzięki krótkim funkcjom każdy test będzie dość prosty.

Nie łatwy sposób testować na niepowodzenie funkcji zależnych

Myślę, że to jedna z największych przeszkód z testów jednostkowych C miałem sukces przy użyciu TestDept, który pozwala uruchomić czas odgałęzienie funkcji zależnych. To świetnie nadaje się do zerwania ściśle powiązanego kodu. Oto przykład z ich dokumentacji:

void test_stringify_cannot_malloc_returns_sane_result() { 
    replace_function(&malloc, &always_failing_malloc); 
    char *h = stringify('h'); 
    assert_string_equals("cannot_stringify", h); 
} 

W zależności od docelowego środowiska może to dla ciebie działać lub nie. Aby uzyskać więcej informacji, patrz their documentation.

Funkcje z wieloma parametrami

to prawdopodobnie nie jest to odpowiedź szukasz, ale chciałbym po prostu rozbić je na mniejsze funkcji przy mniejszej liczbie parametrów. O wiele łatwiejsze do przetestowania.

statyczne lub ukryte funkcje

to nie jest super czysty, ale ja testowałem funkcje statyczne poprzez włączenie pliku źródłowego bezpośrednio, umożliwiając połączenia funkcji statycznych. W połączeniu z TestDeptem do usuwania wszystkiego, czego nie ma w teście, działa to całkiem dobrze.

#include "implementation.c" 

/* Now I can call foo(), defined static in implementation.c */ 

dużo kodu C jest starszy kod z kilku testów - w tych przypadkach, to na ogół łatwiej dodać testy integracyjne sprawdzające dużych części kodu pierwsze, zamiast drobno ziarnistych testów jednostkowych . Pozwala to na rozpoczęcie refaktoryzacji kodu pod testem integracyjnym do stanu testowalnego przez jednostkę - choć może, ale nie musi być warta inwestycji, w zależności od twojej sytuacji.Oczywiście będziesz chciał dodać testy jednostek do każdego nowego kodu napisanego w tym okresie, więc dobrym rozwiązaniem jest posiadanie solidnego szkieletu i wczesnego uruchamiania.

Jeśli jesteś jest działający ze starszym kodem, this book (Efektywna praca ze starszym kodem autorstwa Michaela Feathersa) to świetna dalsza lektura.

+1

Dobra odpowiedź też; +1 ode mnie :-) –

+1

Doskonała odpowiedź! – MarkP

8

To było bardzo dobre pytanie zaprojektowany, by zwabić ludzi do uwierzenia, że ​​C++ jest lepszy niż C, ponieważ jest to bardziej sprawdzalne. Jednak nie jest to takie proste.

Po napisaniu dużej liczby testowalnych kodów C++ i C, a także równie imponującej ilości nietestowalnych kodów C++ i C, mogę śmiało powiedzieć, że można zawinąć nieudolny kod w obu językach. W rzeczywistości większość problemów, które przedstawiłeś powyżej, jest równie problematycznych w C++. EG, wiele osób pisze funkcje nieaplikowane obiektowo w C++ i używa ich wewnątrz klas (patrz rozległe wykorzystanie statycznych funkcji C++ w klasach, jako przykład, takich jak funkcje typu MyAscii :: fromUtf8()).

I jestem całkiem pewien, że widziałaś klasę C++ w gazillion z zbyt wieloma parametrami. A jeśli uważasz, że tylko dlatego, że funkcja ma tylko jeden parametr, lepiej jest rozważyć przypadek, w którym wewnętrznie często maskuje parametry przekazane za pomocą grup zmiennych składowych. Nie mówiąc już o funkcjach "statycznych lub ukrytych" (podpowiedź, pamiętajcie, że słowo kluczowe "prywatne:") jest tak samo dużym problemem.

Tak więc, prawdziwa odpowiedź na twoje pytanie nie brzmi: "C jest gorsze z dokładnie tego, co powinieneś podać", ale "musisz go odpowiednio zaprojektować w C, tak jak w C++". Na przykład, jeśli masz funkcje zależne, umieść je w innym pliku i zwróć liczbę możliwych odpowiedzi, które mogą zapewnić, wprowadzając fałszywą wersję tej funkcji podczas testowania super-funkcji. I to jest ledwo zauważalna zmiana. Nie wykonuj funkcji statycznych lub ukrytych, jeśli chcesz je przetestować.

Prawdziwy problem polega na tym, że zdaje się, że w swoim pytaniu piszesz, że piszesz testy dla cudzej biblioteki, której nie pisałeś, i że jesteś architektem, żeby sprawdzić, czy jest ona sprawna. Istnieje jednak mnóstwo bibliotek C++, które wykazują dokładnie te same objawy i jeśli zostałeś przekazany jednej z nich do przetestowania, byłbyś równie zirytowany.

Rozwiązanie wszystkich takich problemów jest zawsze takie samo: poprawnie wpisz kod i nie używaj niewłaściwie napisanego kodu.

+0

Home run. +1 za mówienie tak, jak jest. –

+0

Dziękuję @RandyHoward, bardzo doceniam ten komplement. –

+0

Świetna rada, ja również przegłosowałem to. Myślę, że C jest mniej wybaczający w dodawaniu testów po fakcie niż w niektórych innych językach, co prowadzi do złej reputacji. Ale możesz * absolutnie * pisać czysto, testowalnie C. –

1

Kiedy testowanie jednostki C zwykle obejmuje plik .c w teście, aby można było przetestować funkcje statyczne przed przetestowaniem publicznych.

Jeśli masz złożone funkcje i chcesz przetestować kod je wywołujący, możesz pracować z próbnymi obiektami. Przyjrzyj się ramce testów jednostkowych cmocka, która oferuje wsparcie dla fałszywych obiektów.

Powiązane problemy