2015-08-13 14 views
6

Jestem nowy dla gmock, więc chcę wiedzieć, w jaki sposób mogę wywołać funkcję prostego C zwaną w funkcji testowanej na testowanie jednostki.Czy można użyć funkcji gmock do skrótów funkcji C?

Przykład:

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

Szukałem o gmock iw moim rozumieniu gmock nie zapewnia funkcjonalność odgałęzienie prostych funkcji C, więc chcę zapytać czy gmock zapewnia funkcjonalność mock lub skrótową func_1?

Jeśli nie, w jaki sposób mogę wstawić kod func_1 ręcznie w moim teście testowym bez zmiany kodu źródłowego? Korzystam z platformy testowej Google do testowania jednostkowego.

Dzięki.

+0

Czy funkcja "func_1()" również jest funkcją "C"? –

+0

@OldFox Tak, jest to funkcja C. – user3159610

+0

Czy 'func_1()' zawiera złożony scenariusz lub używa niezrównanej zależności? (Na przykład sprzęt) –

Odpowiedz

0

W każdym terminalu UT próbujemy zweryfikować konkretne zachowanie.

Powinieneś podrobić coś, gdy jest to bardzo trudne/niemożliwe (musimy wyizolować naszą jednostkę)/spędzić dużo czasu (czasu pracy ..), aby zasymulować określone zachowanie.

Używanie funkcji "C" w sposób jawny oznacza, że ​​funkcja jest niezależna od urządzenia (dlatego nie należy z niego kpić). W odpowiedzi na pytanie this wyjaśniam inicjatywę, aby przetestować metodę tak jak jest (w edycji ...). Moim zdaniem powinieneś zadzwonić pod numer func z parametrami, które powodują symulację zachowania, które chcesz zweryfikować.

GMock jest oparty na fałszywej kompilacji (makrach), dlatego nie można tego zrobić. Aby sfałszować metody "C", musisz użyć różnych narzędzi, takich jak Typemock Isolator++.

Jeśli nie chcesz używać Isolator++, powinieneś refactorować swoją metodę; Zmień func na func(int a, <your pointer the function>), a następnie użyj wskaźnika zamiast func_1.

Mój wykres w odpowiedzi na this może pomóc w podjęciu decyzji o sposobie załatwienia sprawy.

+0

Twoja tabela zawiera dobry punkt widzenia dotyczący tego, ile wysiłku należy poświęcić na testowanie jednostek. Ale mam na myśli pewne pytanie. Co jeśli 'func_1' wskazuje na wiele zależności (funkcji), a następnie testowanie podczas implementacji' func_1', tj. Testowanie integracji może być bardzo czasochłonne. A co jeśli kodu źródłowego 'func' nie można zmienić. Czy istnieje sposób na informowanie kompilatora, aby wskazywał na funkcję kodu pośredniczącego zamiast implementowania prawdziwego 'func_1' bez zmiany kodu źródłowego? – user3159610

+0

@ user3159610 nie ma sposobu, aby powiedzieć kompilatorowi, aby utworzył metodę. Z twojego ostatniego komentarza wynika, że ​​twoja metoda jest starszym kodem. Głównym powodem używania narzędzi do tkania kodu, takich jak 'isolator ++ ', jest starsza wersja kodu. Jeśli jest to starszy kod, proponuję pominąć testy lub utworzyć testy integracyjne tylko dla głównych przepływów. Zgadzam się z tobą na temat testu integracji (czasochłonne), jednak jeśli kod wart jest UT, powinieneś dostać zadanie refaktoryzacji w sprintu lub pieniądze na narzędzia ... –

0

Miałem podobny przypadek w projekcie, w którym byłem jednostkowy. Moim rozwiązaniem było utworzenie dwóch plików make, jednego do produkcji i jednego do testowania.

Jeśli funkcja func_1() jest zdefiniowana w nagłówku a.h i zaimplementowana w a.cpp, to do testowania można dodać nowy plik źródłowy a_testing.cpp, który zaimplementuje wszystkie funkcje w a.h jako kod pośredniczący. Dla unittesting, po prostu skompiluj i połącz z a_testing.cpp zamiast a.cpp, a testowany kod wywoła twój stub.

W a_testing.cpp można następnie przekazać wywołanie do obiektu gmock, który ustawi oczekiwania i działania tak jak zwykle na podstawie stanu i parametrów.

Wiem, że nie jest doskonały, ale działa i rozwiązuje problem bez zmiany kodu produkcyjnego lub interfejsów w ogóle.

7

Ostatnio znalazłem się w tej samej sytuacji. Musiałem napisać testy jednostkowe dla bibliotek napisane w C, które z kolei miały zależności z innymi bibliotekami również napisane w C. Więc chciałem wyśmiać wszystkie wywołania funkcji zależności przy użyciu gmock. Pozwól mi wyjaśnić moje podejście na przykładzie.

Załóżmy kod do testowania (biblioteka A) wywołuje funkcję z innej biblioteki, lib_x_function():

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

Tak, chcę drwić X. biblioteki dlatego napisanie klasy interfejs i makiety Klasa w pliku 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()); 
} 

Dodatkowo utworzyć plik źródłowy (powiedzmy lib_x_mock.cc), która tworzy czop do rzeczywistej funkcji C. To nazwałoby pozorną metodę. Zauważ, że odwołanie do obiektu jest fałszywe.

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

Teraz w pliku testowego, który testuje Library, muszę zdefiniować atrapa obiektu globalnie, tak, że jest osiągalny zarówno w twoich testów iz lib_x_mock.cc. To 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()); 
    ... 
} 

Podejście to działa idealnie dla mnie, a ja mam dziesiątki testów i kilka szydzili bibliotek. Mam jednak kilka wątpliwości, czy tworzenie globalnego obiektu próbnego jest możliwe - poprosiłem o to w separate question i wciąż czekam na odpowiedzi. Poza tym jestem zadowolony z rozwiązania.

+0

Postępowałem zgodnie z twoją procedurą. Jedyną różnicą jest to, że nie ma parametru w danej funkcji. Natomiast dwa parametry w mojej funkcji (jeden to struct, a drugi to int).W interfejsie 'virtual DltReturnValue dlt_client_connect (klient DltClient *, int verbose) = 0;' oraz w pliku testowym 'TEST_F (DltLogClient_test, init) { \t DltClient * client = new DltClient(); \t EXPECT_CALL (MockXIFObj, dlt_client_init (klient, 0)); \t EXPECT_EQ (0, dltLogclient-> init()); } ' Otrzymuję SEGFAULT. Gdzie się mylę? – Nibir

2

Szukałem już długo na rozwiązanie pozornie starszych c-funkcji z googleMock bez zmiany istniejącego kodu i ostatnie dni znalazłem następujący naprawdę świetny artykuł: https://www.codeproject.com/articles/1040972/using-googletest-and-googlemock-frameworks-for-emb

Dzisiaj pisałam testu pierwszej jednostki do c-funkcje za pomocą gmock i wziął jako przykład dwie funkcje z biblioteki bcm2835.c (http://www.airspayce.com/mikem/bcm2835/) dla programowania raspberry Pi: Oto moje rozwiązanie: Używam gcc 4.8.3. w Eclipse i Windows. Bądź świadomy ustawienia opcji kompilatora -std = gnu ++ 11.

Oto moje funkcje mają być testowane

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

Zawiera oraz definiuje dla testów jednostkowych z funkcjami googleTest/googleMock

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

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

Tworzenie 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 szydzili funkcje lib

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

Sztuczne funkcje lib połączyć Mocks z C-funkcje

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

Tworzenie klasy testów jednostkowych dla BCM2835 z TestFixture

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

Napisz do badania przy użyciu googleTest i 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); 
} 

rezultaty :)

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

nadzieję, że pomoże:) - dla mnie to naprawdę działające rozwiązanie.

Powiązane problemy