925 votes

Test unitaire du code C

Cet été, j'ai travaillé sur un système embarqué écrit en C pur. Il s'agissait d'un projet existant que la société pour laquelle je travaille avait repris. J'ai pris l'habitude d'écrire des tests unitaires en Java à l'aide de JUnit, mais je ne savais pas quelle était la meilleure façon d'écrire des tests unitaires pour le code existant (qui devait être remanié) et pour le nouveau code ajouté au système.

Existe-t-il des projets qui rendent les tests unitaires du code C aussi faciles que les tests unitaires du code Java avec JUnit ? Toute idée qui s'appliquerait spécifiquement au développement embarqué (compilation croisée avec la plateforme arm-linux) serait grandement appréciée.

13 votes

Jetez un coup d'œil à cmocka.org

2 votes

@zmo - Recommandations sur les logiciels est le site Stack Exchange pour obtenir des recommandations de logiciels. Je ne l'ai pas utilisé, donc je ne peux pas dire s'il fonctionne bien. Vous devriez vérifier leurs règles de publication avant d'y poster des messages.

544voto

Adam Rosenfield Points 176408

Un cadre de test unitaire en C est Vérifiez ; on peut trouver une liste de cadres de tests unitaires en C ici et est reproduit ci-dessous. Selon le nombre de fonctions de la bibliothèque standard dont dispose votre runtime, vous pourrez ou non utiliser l'une d'entre elles.

AceUnit

AceUnit (Advanced C and Embedded Unit) se présente comme un cadre de test unitaire en code C confortable. Il tente d'imiter JUnit 4.x et inclut des capacités de type réflexion. AceUnit peut être utilisé dans des environnements où les ressources sont limitées, par exemple pour le développement de logiciels embarqués, et surtout, il fonctionne parfaitement dans des environnements où il est impossible d'inclure un seul fichier d'en-tête standard et d'invoquer une seule fonction C standard des bibliothèques C ANSI/ISO. Il dispose également d'un portage Windows. Il n'utilise pas de fourches pour piéger les signaux, bien que les auteurs aient exprimé leur intérêt pour l'ajout d'une telle fonctionnalité. Voir le Page d'accueil de AceUnit .

GNU Autounit

Les mêmes principes que Check, y compris la bifurcation pour exécuter les tests unitaires dans un espace d'adressage séparé (en fait, l'auteur original de Check a emprunté l'idée à GNU Autounit). GNU Autounit utilise largement GLib, ce qui signifie que l'établissement de liens et autres nécessite des options spéciales, mais cela peut ne pas être un gros problème pour vous, surtout si vous utilisez déjà GTK ou GLib. Voir le Page d'accueil de GNU Autounit .

cUnité

Utilise également GLib, mais ne bifurque pas pour protéger l'espace d'adressage des tests unitaires.

CUnit

Standard C, avec des plans pour une implémentation Win32 GUI. Actuellement, il n'y a pas de fork ou de protection de l'espace d'adressage des tests unitaires. En début de développement. Voir le Page d'accueil de CUnit .

CuTest

Un framework simple avec juste un fichier .c et un fichier .h que vous déposez dans votre arbre des sources. Voir le Page d'accueil du CuTest .

CppUnit

Le premier cadre de test unitaire pour C++ ; vous pouvez également l'utiliser pour tester le code C. Il est stable, activement développé et dispose d'une interface graphique. Les principales raisons de ne pas utiliser CppUnit pour le C sont d'abord qu'il est assez gros, et ensuite que vous devez écrire vos tests en C++, ce qui signifie que vous avez besoin d'un compilateur C++. Si ces raisons ne vous préoccupent pas, il vaut la peine de l'envisager, ainsi que d'autres cadres de tests unitaires C++. Voir le Page d'accueil de CppUnit .

embUnit

embUnit (Embedded Unit) est un autre cadre de test unitaire pour les systèmes embarqués. Celui-ci semble être remplacé par AceUnit. Page d'accueil de l'unité embarquée .

MinUnit

Un ensemble minimal de macros et c'est tout ! Le but est de montrer à quel point il est facile de tester son code. Voir le Page d'accueil de MinUnit .

CUnité pour M. Ando

Une implémentation de CUnit qui est assez nouvelle et apparemment encore en phase de développement précoce. Voir le CUnité pour la page d'accueil de M. Ando .

0 votes

Au départ, le chèque semble très solide. Je devrai voir s'il résiste à l'épreuve de l'utilisation réelle... mais il semble bien qu'il puisse faire l'affaire.

8 votes

Nous utilisons check pour les tests unitaires du code sur nos systèmes embarqués. Dans la plupart des cas, check était un bon choix mais maintenant nous travaillons sur des systèmes fonctionnant sous uClinux et comme check nécessite fork, il ne fonctionne pas sur ces systèmes. :/

0 votes

Il semble que CUnit soit plus actif. La version de Check fournie dans le repo Ubuntu pour 12.04 est boguée.

178voto

mikelong Points 2034

Personnellement, j'aime le Cadre de test Google .

La vraie difficulté pour tester du code C est de briser les dépendances sur les modules externes afin de pouvoir isoler le code en unités. Cela peut être particulièrement problématique lorsque vous essayez de tester du code hérité. Dans ce cas, je me retrouve souvent à utiliser le linker pour utiliser des fonctions stubs dans les tests.

C'est à cela que les gens font référence lorsqu'ils parlent de " coutures ". En C, votre seule option est d'utiliser le préprocesseur ou l'éditeur de liens pour déterminer vos dépendances.

Une suite de tests typique dans un de mes projets C peut ressembler à ceci :

#include "myimplementationfile.c"
#include <gtest/gtest.h>

// Mock out external dependency on mylogger.o
void Logger_log(...){}

TEST(FactorialTest, Zero) {
    EXPECT_EQ(1, Factorial(0));
}

Notez que vous incluez en fait le fichier C et non le fichier d'en-tête. . Cela donne l'avantage d'avoir accès à tous les membres de données statiques. Ici, je simule mon logger (qui pourrait être dans logger.o) et je donne une implémentation vide. Cela signifie que le fichier de test est compilé et lié indépendamment du reste du code de base et s'exécute de manière isolée.

En ce qui concerne la compilation croisée du code, pour que cela fonctionne, vous devez disposer de bonnes installations sur la cible. Je l'ai fait avec googletest compilé de manière croisée avec Linux sur une architecture PowerPC. Cela a du sens car vous disposez d'un shell complet et d'un système d'exploitation pour recueillir vos résultats. Pour les environnements moins riches (que je classe comme tout ce qui n'a pas de système d'exploitation complet), vous devriez simplement construire et exécuter sur l'hôte. Vous devriez le faire de toute façon pour pouvoir exécuter les tests automatiquement dans le cadre de la construction.

Je trouve que tester du code C++ est généralement beaucoup plus facile, car le code OO est en général beaucoup moins couplé que le code procédural (bien sûr, cela dépend beaucoup du style de codage). En outre, en C++, vous pouvez utiliser des astuces comme l'injection de dépendances et le remplacement de méthodes pour obtenir des coutures dans du code qui est autrement encapsulé.

Michael Feathers a un excellent livre sur le test des codes hérités . Dans un chapitre, il aborde les techniques de traitement du code non OO, ce que je recommande vivement.

Modifier : J'ai écrit un article de blog sur les tests unitaires de code procédural, avec source disponible sur GitHub .

Modifier : Il y a un Le nouveau livre des programmeurs pragmatiques va sortir. qui traite spécifiquement des tests unitaires du code C qui Je recommande vivement .

19 votes

N'achetez pas le livre prag. prog. Il ne contient aucune information qui ne figure pas dans les réponses à cette question.

4 votes

Je sais que le C et le C++ se chevauchent beaucoup, mais cela ne me semble pas être une bonne idée d'utiliser une bibliothèque de tests C++ lorsque vous produisez du code qui sera finalement compilé dans un compilateur C.

2 votes

@RafaelAlmeida en substance je suis d'accord, ce que je montre ici est une couture de préprocesseur sans envelopper l'include C dans un extern C. Indépendamment de cela, j'ai trouvé le C++ assez pratique comme langage de description de test dans la pratique. J'ai également écrit un cadre de test basé sur le C, donc je ne suis pas dogmatique à ce sujet :-) github.com/meekrosoft/fff

143voto

Matteo Caprari Points 1195

Minunit est un cadre de test unitaire incroyablement simple. Je l'utilise pour tester le code d'un microcontrôleur c pour avr.

6 votes

Je n'ai aucune expérience en matière de systèmes embarqués et je ne peux donc pas faire de commentaires à ce sujet, mais pour les petits programmes C (travaux scolaires, scripts), cela semble parfait. Excellent lien.

3 votes

@toasted_flakes J'ai transformé ça en un gist github : gist.github.com/sam159/0849461161e86249f849

0 votes

C'est assez proche de ce que j'ai trouvé avant de commencer à chercher ici ! J'aimerais automatiser les tests de façon à ce que TEST(funcname, body) crée la fonction et stocke un pointeur vers la fonction, mais il semble que je doive effectuer un traitement externe.

43voto

Michael Burr Points 181287

J'utilise actuellement le cadre de test unitaire CuTest :

http://cutest.sourceforge.net/

Il est idéal pour les systèmes embarqués car il est très léger et simple. Je n'ai eu aucun problème à le faire fonctionner sur la plateforme cible ainsi que sur le bureau. En plus de l'écriture des tests unitaires, tout ce qui est nécessaire est :

  • un fichier d'en-tête inclus partout où vous appelez les routines CuTest.
  • un seul fichier 'C' supplémentaire qui doit être compilé/lié à l'image
  • un peu de code simple ajouté à la main pour pour mettre en place et appeler les tests unitaires - je l'ai simplement placé dans une fonction spéciale main() spéciale qui est compilée si UNITTEST est défini pendant la construction.

Le système doit supporter un tas et quelques fonctionnalités stdio (ce que tous les systèmes embarqués ne possèdent pas). Mais le code est assez simple pour que vous puissiez probablement travailler avec des alternatives à ces exigences si votre plateforme ne les a pas.

Avec une utilisation judicieuse des blocs extern "C"{}, il permet également de tester le C++ sans problème.

1 votes

J'appuie le vote pour CuTest. Je l'ai utilisé pour développer des homebrews sur la Nintendo DS et je n'ai eu aucune difficulté à le configurer ou à l'utiliser.

0 votes

Je vais m'avancer sur ce point. Je l'ai téléchargé quand c'était la version 1.4 et je l'ai modifié pour qu'il soit vidé en XML. Il semble qu'il y ait une version 1.5 que je vais devoir télécharger et regarder.

2 votes

CuTest a bien fonctionné pour moi pour tester le code fonctionnant sur un système QNX.

37voto

Ovid Points 7256

Vous pouvez également jeter un coup d'œil à libtap Test Anything Protocol (TAP), un cadre de test en C qui utilise le protocole Test Anything Protocol (TAP) et s'intègre donc bien à une variété d'outils qui sortent pour cette technologie. Il est surtout utilisé dans le monde des langages dynamiques, mais il est facile à utiliser et devient très populaire.

Un exemple :

#include <tap.h>

int main () {
    plan(5);

    ok(3 == 3);
    is("fnord", "eek", "two different strings not that way?");
    ok(3 <= 8732, "%d <= %d", 3, 8732);
    like("fnord", "f(yes|no)r*[a-f]$");
    cmp_ok(3, ">=", 10);

    done_testing();
}

0 votes

J'ai développé à la main mon propre équivalent de libtap pour mes propres projets, mais maintenant que je sais que cela existe, je n'aurai plus à maintenir le mien. Cool !

1 votes

ok(TESTING==IsSimple(), "libtap is super easy to use")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X