2015-08-13 1 views
6

Je suis nouveau à gmock, donc je veux savoir comment puis-je stub simple fonction C appelée dans une fonction testée pour le test unitaire.Peut-on utiliser gmock pour écraser les fonctions C?

Exemple:

int func(int a) 
{ 
    boolean find; 
    // Some code 
    find = func_1(); 
    return find; 
} 

J'ai recherché sur les gmock et dans mon gmock compréhension ne fournit pas la fonctionnalité à bouchonner fonctions simples C, donc je veux demander à ne gmock fournit une fonctionnalité de railler ou stub func_1?

Sinon comment puis-je talonner func_1 manuellement dans mon code de test sans changer le code source? J'utilise le framework de test google pour les tests unitaires.

Merci.

+0

Est-ce que 'func_1()' est aussi une fonction 'C'? –

+0

@OldFox Oui c'est une fonction C. – user3159610

+0

Est-ce que 'func_1()' fournit un scénario complexe ou utilise une dépendance untestable? (Par exemple le matériel) –

Répondre

0

Dans chaque UT nous essayons de vérifier un comportement spécifique.

Vous devez truquer quelque chose quand c'est très difficile/impossible (nous devons isoler notre unité)/passer beaucoup de temps (temps de fonctionnement ..) pour simuler un comportement spécifique. L'utilisation explicite d'une fonction «C» signifie que la fonction est séparée de votre unité (vous ne devez donc pas vous moquer de celle-ci ..). Dans this réponse, j'explique l'initiative pour tester la méthode telle quelle (dans l'édition ..). À mon avis, vous devriez appeler func avec les paramètres qui causent func_1 pour simuler le comportement que vous voulez vérifier. Est basé sur la compilation fake (macros), donc vous ne pouvez pas faire une telle chose.

GMock Pour fausser les méthodes 'C', vous devez utiliser un outil différent tel que Typemock Isolator++.

Si vous ne voulez pas utiliser Isolator++, vous devez refactoriser votre méthode; Remplacez func par func(int a, <your pointer the function>), puis utilisez le pointeur au lieu de func_1.

Mon tableau en this réponse pourrait aider à décider de la façon de gérer votre cas.

+0

Votre graphique fournit un bon point de vue sur la quantité d'efforts à mettre dans les tests unitaires. Mais j'ai certaines questions en tête. Que se passe-t-il si 'func_1' pointe vers de nombreuses dépendances (fonctions) et que les tests d'intégration lors de l'implémentation de' func_1', c'est-à-dire les tests d'intégration, peuvent prendre beaucoup de temps. Et si le code source 'func' ne peut pas être changé. Existe-t-il un moyen de dire au compilateur de pointer vers une fonction de stub au lieu d'implémenter le vrai func_1 sans changer le code source? – user3159610

+0

@ user3159610 Il n'y a aucun moyen de dire au compilateur de boucher la méthode. De votre dernier commentaire semble que votre méthode est un code hérité. La principale raison d'utiliser des outils de tissage de code comme 'isolator ++' est le code hérité. S'il s'agit d'un code existant, je vous proposerais d'ignorer les tests ou de créer un test d'intégration uniquement pour les flux principaux. Je suis d'accord avec vous sur le test d'intégration (qui prend du temps), mais si le code vaut UT vous devriez obtenir une tâche de refactoring dans le sprint ou l'argent pour les outils .... –

0

J'ai eu un cas similaire dans un projet, je test par unité. Ma solution était de créer deux fichiers make, un pour la production et un pour les tests. Si la fonction func_1() est définie dans l'en-tête a.h et implémentée dans a.cpp, vous pouvez ajouter un nouveau fichier source a_testing.cpp, qui implémentera toutes les fonctions dans a.h comme un stub. Pour unittesting, il suffit de compiler et de lier avec a_testing.cpp au lieu de a.cpp et le code testé appellera votre stub. Dans a_testing.cpp, vous pouvez ensuite transférer l'appel à un objet gmock qui définira les attentes et les actions comme d'habitude en fonction de l'état et des paramètres.

je sais que ce n'est pas parfait, mais il fonctionne résoudre ans le problème sans changer le code de production ou des interfaces du tout.

7

je me suis retrouvé dans la même situation dernièrement. J'ai dû écrire des tests unitaires pour bibliothèques écrites en C, qui à leur tour avaient des dépendances à d'autres bibliothèques écrites en C. Donc je voulais me moquer de tous les appels de dépendances de fonction en utilisant gmock. Laissez-moi vous expliquer mon approche par un exemple.

Supposons que le code à tester (bibliothèque A) appelle une fonction à une autre bibliothèque, lib_x_function():

lib_a_function() 
{ 
    ... 
    retval = lib_x_function(); 
    ... 
} 

Alors, je veux railler la bibliothèque X. Par conséquent j'écris une classe d'interface et une maquette classe dans un fichier lib_x_mock.h:

class LibXInterface { 
public: 
    virtual ~LibXInterface() {} 
    virtual int lib_x_function() = 0; 
} 

class LibXMock : public LibXInterface { 
public: 
    virtual ~LibXMock() {} 
    MOCK_METHOD0(lib_x_function, int()); 
} 

outre créer un fichier de source (par exemple, lib_x_mock.cc), qui définit un talon pour la fonction réelle C. Cela appellera la méthode fictive. Notez la référence extern à l'objet maquette.

#include lib_x.h 
#include lib_x_mock.h 
extern LibXMock LibXMockObj; /* This is just a declaration! The actual 
            mock obj must be defined globally in your 
            test file. */ 

int lib_x_function() 
{ 
    return LibXMockObj.lib_x_function(); 
} 

Maintenant, dans le fichier de test, qui teste la bibliothèque A, je dois définir l'objet fantaisie globalement, de sorte qu'il est à la fois accessible en vos tests et de lib_x_mock.cc. Ceci est lib_a_tests.cc:

#include lib_x_mock.h 

LibXMock LibXMockObj; /* This is now the actual definition of the mock obj */ 

... 
TEST_F(foo, bar) 
{ 
    EXPECT_CALL(LibXMockObj, lib_x_function()); 
    ... 
} 

Cette approche fonctionne parfaitement pour moi, et j'ai des dizaines de tests et plusieurs bibliothèques moqués. Cependant, j'ai quelques doutes s'il est permis de créer un global objet mock - j'ai demandé cela dans un separate question et toujours attendre des réponses. De plus, je suis content de la solution.

+0

J'ai suivi votre procédure. La seule différence est qu'il n'y a pas de paramètre dans votre fonction donnée. En revanche, deux paramètres dans ma fonction (l'un est struct et l'autre est int).Dans l'interface, 'virtual DltReturnValue dlt_client_connect (client DltClient *, int verbose) = 0;' et dans le fichier de test 'TEST_F (DltLogClient_test, init) { \t DltClient * client = nouveau DltClient(); \t EXPECT_CALL (MockXIFObj, dlt_client_init (client, 0)); \t EXPECT_EQ (0, dltLogclient-> init()); } ' Je reçois un SEGFAULT. Où est-ce que je me trompe? – Nibir

2

Je cherchais déjà depuis longtemps pour une solution à l'héritage simulacres c-fonctions avec googleMock sans changer le code existant et derniers jours, j'ai trouvé l'article suivant vraiment super: https://www.codeproject.com/articles/1040972/using-googletest-and-googlemock-frameworks-for-emb

Aujourd'hui j'ai écrit mon premier test unitaire pour c-fonctions en utilisant gmock et a pris comme exemple deux fonctions de la bibliothèque bcm2835.c (http://www.airspayce.com/mikem/bcm2835/) pour la programmation Raspberry Pi: Voici ma solution: J'utilise le gcc 4.8.3. sous Eclipse et Windows. Soyez conscient de définir l'option du compilateur -std = gnu ++ 11.

Voici mes fonctions à tester

int inits(void); 
void pinMode(uint8_t pin, uint8_t mode); 

int inits(){ 
    return bcm2835_init(); 
} 

void pinMode(uint8_t pin, uint8_t mode){ 
    bcm2835_gpio_fsel(pin, mode); 
} 

Comprend et définit pour les tests unitaires avec googleTest/googleMock

// MOCKING C-Functions with GMOCK :) 
#include <memory> 
#include "gtest/gtest.h" 
#include "gmock/gmock.h" 
using namespace ::testing; 
using ::testing::Return; 

Mock fonctions BCM2835Lib

class BCM2835Lib_MOCK{ 
public: 
    virtual ~BCM2835Lib_MOCK(){} 

    // mock methods 
    MOCK_METHOD0(bcm2835_init,int()); 
    MOCK_METHOD2(bcm2835_gpio_fsel,void(uint8_t,uint8_t)); 
}; 

Insérer un TestFixture

class TestFixture: public ::testing::Test{ 
public: 
    TestFixture(){ 
     _bcm2835libMock.reset(new ::testing::NiceMock<BCM2835Lib_MOCK>()); 
    } 
    ~TestFixture(){ 
     _bcm2835libMock.reset(); 
    } 
    virtual void SetUp(){} 
    virtual void TearDown(){} 

    // pointer for accessing mocked library 
    static std::unique_ptr<BCM2835Lib_MOCK> _bcm2835libMock; 
}; 

instancier moqué fonctions lib

// instantiate mocked lib 
std::unique_ptr<BCM2835Lib_MOCK> TestFixture::_bcm2835libMock; 

fonctions Faux lib pour connecter Mocks avec les fonctions c-

// fake lib functions 
int bcm2835_init(){return TestFixture::_bcm2835libMock->bcm2835_init();} 
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode){TestFixture::_bcm2835libMock->bcm2835_gpio_fsel(pin,mode);} 

Créer une classe de test unitaire pour BCM2835 de TestFixture

// create unit testing class for BCM2835 from TestFixture 
class BCM2835LibUnitTest : public TestFixture{ 
public: 
    BCM2835LibUnitTest(){ 
     // here you can put some initializations 
    } 
}; 

écrire des tests utilisant googleTest et googleMock

TEST_F(BCM2835LibUnitTest,inits){ 
    EXPECT_CALL(*_bcm2835libMock,bcm2835_init()).Times(1).WillOnce(Return(1)); 

    EXPECT_EQ(1,inits()) << "init must return 1"; 
} 

TEST_F(BCM2835LibUnitTest,pinModeTest){ 

    EXPECT_CALL(*_bcm2835libMock,bcm2835_gpio_fsel((uint8_t)RPI_V2_GPIO_P1_18 
                ,(uint8_t)BCM2835_GPIO_FSEL_OUTP 
               ) 
       ) 
       .Times(1) 
       ; 

    pinMode((uint8_t)RPI_V2_GPIO_P1_18,(uint8_t)BCM2835_GPIO_FSEL_OUTP); 
} 

Résultats :)

[----------] 2 tests from BCM2835LibUnitTest 
[ RUN  ] BCM2835LibUnitTest.inits 
[  OK ] BCM2835LibUnitTest.inits (0 ms) 
[ RUN  ] BCM2835LibUnitTest.pinModeTest 
[  OK ] BCM2835LibUnitTest.pinModeTest (0 ms) 
[----------] 2 tests from BCM2835LibUnitTest (0 ms total) 

espère que cela vous aidera:) - pour moi, c'est une solution vraiment efficace.