2010-02-26 13 views
18

Je suis un développeur de logiciels relativement nouveau qui travaille actuellement à ajouter des tests unitaires à un projet C++ existant qui a débuté il y a des années. Pour des raisons non techniques, je ne suis pas autorisé à modifier un code existant. La classe de base de tous mes modules a un tas de méthodes pour définir/obtenir des données et communiquer avec d'autres modules.Se moquer des méthodes non-virtuelles en C++ sans éditer le code de production?

Puisque je veux juste tester chaque module individuellement, je veux pouvoir utiliser des valeurs prédéfinies pour toutes mes méthodes de communication inter-modules. C'est à dire. pour une méthode Ping() qui vérifie si un autre module est actif, je veux qu'il retourne vrai ou faux selon le type de test que je fais. J'ai regardé dans Google Test et Google Mock, et il prend en charge les méthodes non-virtuelles moqueuses. Cependant l'approche décrite (http://code.google.com/p/googlemock/wiki/CookBook#Mocking_Nonvirtual_Methods) m'oblige à "templacer" les méthodes originales pour prendre des objets réels ou faux. Je ne peux pas aller créer des modèles à mes méthodes dans la classe de base en raison de l'exigence mentionnée plus haut, donc je besoin d'une autre façon de se moquer de ces méthodes virtuelles

Fondamentalement, les méthodes que je veux moquer sont dans une classe de base, la modules Je veux tester unitairement et créer des mocks de classes dérivées de cette classe de base. Il y a des modules intermédiaires entre ma classe de module de base et les modules que je veux tester.

J'apprécierais n'importe quel conseil!

Merci,

JW

EDIT: A plus d'exemples concrets

Ma classe de base est permet de dire rootModule, le module que je veux tester est leafModule. Il y a un module intermédiaire qui hérite de rootModule, leafModule hérite de ce module intermédiaire.

Dans mon leafModule, je veux tester la méthode doStuff(), qui appelle le GetStatus non virtuel (moduleName) défini dans la classe rootModule. Je dois en quelque sorte faire GetStatus() pour retourner une valeur prédéfinie choisie. Mocking est nouveau pour moi, alors utiliser des objets simulés, même la bonne approche?

+0

Pouvez-vous modifier localement le code juste pour vos tests? Vous n'auriez pas à enregistrer les modifications. –

+0

Etes-vous obligé d'utiliser des tests unitaires? Il existe d'autres méthodes (automatisées) de test de régression. Les tests unitaires sont un outil, pas une religion - si cela ne convient pas, ne l'utilisez pas. Idéalement – ima

+0

mon équipe souhaite utiliser les tests unitaires, car notre arbre module est assez grand, nous vraiment envie de découpler les tests de fonctionnalité inter-module de l'essai des méthodes locales d'un module. Il est regrettable que nos décisions de conception antérieures aient rendu les tests unitaires difficiles. À quelles autres méthodes de test de régression faites-vous référence? J'imagine que, en utilisant d'autres méthodologies de test, nous aurons toujours besoin d'un moyen d'avoir des méthodes non-virtuelles ou de retourner des valeurs prédéfinies, ce qui signifie que nous trouverons une solution ou garderons un autre arbre source. – wk1989

Répondre

3

Je voudrais écrire un script Perl/Ruby/Python à lire dans l'arborescence source originale et écrire une arborescence source mocked dans un répertoire différent. Vous n'avez pas besoin d'analyser complètement C++ pour remplacer une définition de fonction.

+3

Cela fonctionnerait probablement, mais il serait plus simple pour l'OMI d'avoir seulement 2 arbres sources et dans l'un d'entre eux, de changer les méthodes de la classe de base pour qu'elles soient virtuelles. Il y aurait quelques inconvénients affectant les gars qui sont en charge du système de contrôle de la source. Les développeurs devront également s'ajuster à l'écriture de code pour 2 révisions de travail. Ce que je cherche est une solution de contournement qui va me permettre d'intégrer mes tests unitaires dans un seul arbre source qui peut être construit (et exécuté) en une seule passe. Ce dont je ne suis pas sûr, c'est si c'est possible. – wk1989

+1

@ wk1989: Les développeurs ne travailleraient jamais sur deux arbres source avec l'idée de script. C'est tout le point. Toute l'édition de code serait faite sur l'arbre de code réel. Les tests seraient exécutés à partir de l'arbre de test généré par le script. –

1

Une approche consisterait à spécifier différentes sources pour les tests. Supposons que votre cible de production utilise rootModule.h et rootModule.cpp. Utilisez différentes sources pour votre cible de test. Vous pouvez spécifier un en-tête différent en changeant votre chemin d'inclusion, afin que #include "rootModule.h" charge effectivement unittest/rootModule.h. Puis se moquer rootModule au contenu de votre coeur.

12

Il existe différentes manières de remplacer des fonctions non virtuelles. L'une consiste à les déclarer à nouveau et à compiler un nouvel exécutable de test pour chaque ensemble différent de fonctions non virtuelles que vous souhaitez tester. C'est difficilement scalable.

Une deuxième option consiste à les rendre virtuels pour le test. La plupart des compilateurs vous permettent de définir quelque chose sur la ligne de commande afin de compiler votre code avec -DTEST_VIRTUAL = virtual ou -DTEST_VIRTUAL pour les rendre soit virtuels soit normaux selon qu'ils sont en test ou non.

Une troisième option qui peut être utilisable est d'utiliser un cadre de simulation qui vous permet de vous moquer des fonctions non virtuelles. Je suis l'auteur de HippoMocks (avertissement concernant la neutralité et ainsi de suite) et nous avons récemment ajouté la possibilité de se moquer des fonctions C simples sur les plates-formes X86.Cela peut être étendu à des fonctions membres non virtuelles avec un peu de travail et serait ce que vous cherchez. Gardez à l'esprit que, si votre compilateur peut voir à la fois l'utilisation et la définition d'une fonction à la fois qu'il peut en ligne et que le moqueur peut échouer. Cela vaut en particulier pour les fonctions définies dans les en-têtes.

Si moqueur régulier de la fonction C est suffisante pour vous, vous pouvez l'utiliser comme il est maintenant.

+1

Je viens de rencontrer cette réponse. J'ai l'intention d'utiliser HippoMocks pendant un certain temps maintenant (première fois à l'aide d'un cadre moqueur), mais je me penche vers la compilation injection de dépendance à l'aide de modèles, et je ne sais pas si HippoMocks peut gérer cela. Pouvez-vous expliquer un peu plus loin ce qui serait nécessaire pour se moquer des fonctions membres non-virtuelles? Et pourquoi inlining ferait échouer le moqueur? – JBentley

+0

fonctions membres non virtuelles peuvent être linéarisés soit par le compilateur lors de la compilation d'une unité, ou par le groupe de liaison au moment de la liaison (LTO). Les deux signifient que l'adresse d'une fonction n'est plus bien définie, donc le remplacer par quelque chose d'autre est également impossible car vous ne pouvez pas trouver ces autres "nouveaux" emplacements. Pour les fonctions membres virtuelles, vous avez la certitude que cela ne se produira pas (étant donné qu'elles sont virtuelles, il n'y a pas d'inlining possible). – dascandy

+0

fonctions non virtuelles sont moquaient en remplaçant les n premiers octets de la fonction - littéralement piratage du code lors de l'exécution - avec un saut à une fonction simulée. Cela ne fonctionne que s'il n'y a qu'un seul emplacement de la fonction à éditer; vous ne pouvez pas avoir un pointeur de fonction sur plusieurs emplacements. Répondre divisé en deux commentaires car ils sont limités en longueur. – dascandy