2015-08-13 8 views
6

Ich bin neu bei Gmock, also möchte ich wissen, wie ich einfache C-Funktion, die in einer Funktion im Test für Unit Testing aufgerufen wird, stub.Kann Gmock für Stubbing C-Funktionen verwendet werden?

Beispiel:

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

Ich habe über gmock gesucht und in meinem Verständnis gmock Funktionalität bietet keine einfache C-Funktionen Stummel, daher möchte ich fragen nicht gmock Funktionalität oder Stummel func_1 zu verspotten bietet?

Wenn nicht wie kann ich manuell func_1 in meinem Testcode stub, ohne den Quellcode zu ändern? Ich benutze Google Test Framework für Komponententests.

Danke.

+0

Ist 'func_1()' auch eine 'C'-Funktion? –

+0

@OldFox Ja, es ist eine C-Funktion. – user3159610

+0

Ist 'func_1()' ein komplexes Szenario oder verwenden Sie eine nicht testbare Abhängigkeit (zB Hardware) –

Antwort

0

In jedem UT versuchen wir, ein bestimmtes Verhalten zu überprüfen.

Sie sollten etwas fälschen, wenn es sehr schwer/unmöglich ist (wir müssen unser Gerät isolieren)/verbringen Sie viel Zeit (Laufzeit ..), um ein bestimmtes Verhalten zu simulieren.

Die Verwendung einer 'C'-Funktion auf die explizite Weise bedeutet, dass die Funktion von Ihrer Einheit getrennt ist (daher sollten Sie es nicht verspotten ..). In this Antwort erkläre ich die Initiative, um die Methode zu testen, wie es ist (in der Bearbeitung ..). Meiner Meinung nach sollten Sie func mit den Parametern aufrufen, die func_1 verursachen, um das Verhalten zu simulieren, das Sie überprüfen möchten.

GMock basiert auf Compilation Fake (Makros), daher können Sie so etwas nicht tun. Um "C" -Methoden vorzutäuschen, müssen Sie ein anderes Werkzeug wie Typemock Isolator++ verwenden.

Wenn Sie nicht Isolator++ verwenden möchten, sollten Sie Ihre Methode umgestalten; Ändern Sie func in func(int a, <your pointer the function>) und verwenden Sie dann den Zeiger anstelle von func_1.

Mein Diagramm in this Antwort kann helfen, die Art und Weise zu entscheiden, Ihren Fall zu behandeln.

+0

Ihr Diagramm bietet einen guten Überblick darüber, wie viel Aufwand in Unit Testing erforderlich ist. Aber ich habe bestimmte Fragen im Hinterkopf. Was ist, wenn 'func_1' auf viele Abhängigkeiten (Funktionen) zeigt und dann beim Implementieren von' func_1' testet, d. H. Integration Testing könnte sehr zeitaufwendig sein. Und was, wenn der Quellcode 'func' nicht geändert werden kann. Gibt es eine Möglichkeit, dem Compiler mitzuteilen, dass er auf eine Stub-Funktion zeigen soll, anstatt "func_1" zu implementieren, ohne den Quellcode zu ändern? – user3159610

+0

@ user3159610 gibt es keine Möglichkeit, dem Compiler mitzuteilen, die Methode zu stubben. Aus Ihrem letzten Kommentar scheint, dass Ihre Methode ein Legacy-Code ist. Der Hauptgrund, Code-Weben-Tools wie 'isolator ++' zu verwenden, ist Legacy-Code. Wenn es sich um einen Legacy-Code handelt, würde ich Ihnen anbieten, die Tests zu überspringen oder nur für die Hauptabläufe einen Integrationstest zu erstellen. Ich stimme Ihnen mit dem Integrationstest (zeitaufwendig) zu, aber wenn der Code UT wert ist, sollten Sie eine Refactoring-Aufgabe im Sprint oder das Geld für Tools bekommen .... –

0

Ich hatte einen ähnlichen Fall in einem Projekt, das ich Unit-Tests war. Meine Lösung bestand darin, zwei Make-Dateien zu erstellen, eine für die Produktion und eine für das Testen.

Wenn die Funktion func_1() im Header a.h definiert und in a.cpp implementiert ist, dann können Sie zum Testen eine neue Quelldatei a_testing.cpp hinzufügen, die alle Funktionen in a.h als Stub implementiert. Für Unittests, kompilieren und verknüpfen Sie einfach mit a_testing.cpp anstelle von a.cpp und der getestete Code wird Ihren Stub aufrufen.

In a_testing.cpp können Sie dann den Aufruf an ein gmock-Objekt weiterleiten, das basierend auf dem Status und den Parametern die Erwartungen und Aktionen wie gewohnt festlegt.

Ich weiß, es ist nicht perfekt, aber es funktioniert und löst das Problem, ohne Produktionscode oder Schnittstellen überhaupt zu ändern.

7

Ich fand mich in letzter Zeit in der gleichen Situation. Ich musste Komponententests für Bibliotheken geschrieben in C schreiben, die wiederum Abhängigkeiten zu anderen Bibliotheken auch in C geschrieben hatte. Also wollte ich alle Funktionsaufrufe von Abhängigkeiten mit gmock Mock machen. Lassen Sie mich meinen Ansatz an einem Beispiel erläutern.

den Code Angenommen zu prüfenden (Bibliothek A) werden, um eine Funktion aus einer anderen Bibliothek aufruft, lib_x_function():

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

Also, ich möchte daher die Bibliothek X. verspotten ich eine Interface-Klasse und eine Mock schreiben Klasse in einer Datei 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()); 
} 

Zusätzlich schaffen I eine Quelldatei (sagen wir, lib_x_mock.cc), die einen Stub für die eigentliche Funktion C definiert. Dies soll die Scheinmethode aufrufen. Beachten Sie die extern Referenz auf das Scheinobjekt.

#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(); 
} 

nun in der Testdatei, die die Bibliothek A testet, muss ich das Mock-Objekt global definieren, so dass sie beide zu Fuß in Ihren Tests und von lib_x_mock.cc ist. Dies ist 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()); 
    ... 
} 

Dieser Ansatz funktioniert perfekt für mich, und ich habe Dutzende von Tests und mehrere verspottet Bibliotheken. Allerdings habe ich ein paar Zweifel, ob es in Ordnung ist, ein globales Mock-Objekt zu erstellen - ich fragte dies in einem separate question und warte immer noch auf Antworten. Außerdem bin ich glücklich mit der Lösung.

+0

Ich folgte Ihrem Verfahren. Der einzige Unterschied ist, dass es in Ihrer gegebenen Funktion keinen Parameter gibt. Im Gegensatz dazu sind zwei Parameter in meiner Funktion (eine ist struct und eine andere ist int).In der Schnittstelle 'virtual DltReturnValue dlt_client_connect (DltClient * client, int verbose) = 0;' und in der Testdatei 'TEST_F (DltLogClient_test, init) { \t DltClient * client = new DltClient(); \t EXPECT_CALL (MockXIFObj, dlt_client_init (Client, 0)); \t EXPECT_EQ (0, dltLogclient-> init()); } ' Ich bekomme ein SEGFAULT. Wo habe ich falsch gemacht? – Nibir

2

ich bereits nach einer Lösung für Mock Vermächtnis c-Funktionen mit googleMock ohne Änderung der bestehenden Code und letzten Tage eine lange Zeit war auf der Suche ich folgendes wirklich großen Artikel gefunden: https://www.codeproject.com/articles/1040972/using-googletest-and-googlemock-frameworks-for-emb

Heute habe ich für meine erste Einheit Test geschrieben c-Funktionen mit gmock und nahm als Beispiel zwei Funktionen aus der Bibliothek bcm2835.c (http://www.airspayce.com/mikem/bcm2835/) für Raspberry Pi Programmierung: Hier ist meine Lösung: Ich benutze die gcc 4.8.3. unter Eclipse und Windows. Achten Sie darauf, die Compiler-Option -std = gnu ++ 11 festzulegen.

Hier sind meine Funktionen

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); 
} 

Inklusive und für Unit-Tests definiert mit googleTest/googleMock

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

Funktionen Mock 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)); 
}; 
getestet werden

erstellen 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; 
}; 

Instantiate verspottet lib Funktionen

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

Gefälschte lib Funktionen Mocks verbinden mit den c-Funktionen

// 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);} 

erstellen Unit-Test-Klasse für BCM2835 von TestFixture

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

die Tests schreiben googleTest und 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); 
} 

Ergebnisse :)

[----------] 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) 

Hoffe, es wird dazu beitragen, mit:) - für mich ist das eine wirklich funktionierende Lösung.

Verwandte Themen