¿Se puede usar gmock para agregar funciones de C?

9 minutos de lectura

avatar de usuario
SRehman

Soy nuevo en gmock, así que quiero saber cómo puedo ejecutar una función C simple llamada en una función bajo prueba para Pruebas unitarias.

Ejemplo:

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

He buscado sobre gmock y, según tengo entendido, gmock no proporciona funcionalidad para funciones simples de C, por lo tanto, quiero preguntar si gmock proporciona funcionalidad para simular o stub. func_1?

Si no, ¿cómo puedo cortar? func_1 manualmente en mi código de prueba sin cambiar el código fuente? Estoy usando el marco de prueba de Google para pruebas unitarias.

Gracias.

  • Es func_1() también una función ‘C’?

    – Viejo zorro

    13 de agosto de 2015 a las 13:58

  • @OldFox Sí, es una función C.

    – SRehman

    13/08/2015 a las 14:00

  • Es func_1() proporciona un escenario complejo o utiliza una dependencia no comprobable? (por ejemplo, hardware)

    – Viejo zorro

    13 de agosto de 2015 a las 14:07


  • Bueno, acabo de dar un ejemplo como ‘func_1()’. Puede estar implementando un escenario simple o complejo. Sin embargo, en mi caso no hay una dependencia no comprobable.

    – SRehman

    13 de agosto de 2015 a las 14:11

avatar de usuario
Jorge P.

Me encontré en la misma situación últimamente. Tuve que escribir pruebas unitarias para bibliotecas escritas en C, que a su vez tenían dependencias con otras bibliotecas también escritas en C. Así que quería simular todas las llamadas a funciones de dependencias usando simulacro. Permítanme explicar mi enfoque con un ejemplo.

Suponga que el código a probar (biblioteca A) llama a una función de otra biblioteca, lib_x_function():

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

Entonces, quiero simular la biblioteca X. Por lo tanto, escribo una clase de interfaz y una clase simulada en un archivo 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());
}

Además, creo un archivo fuente (digamos, lib_x_mock.cc), que define un código auxiliar para la función C real. Esto llamará al método simulado. Nota la extern
referencia al objeto simulado.

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

Ahora, en el archivo de prueba, que prueba la biblioteca A, debo definir el objeto simulado
globalmentepara que sea accesible dentro de sus pruebas y desde
lib_x_mock.cc. Este es 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());
   ...
}

Este enfoque funciona perfectamente para mí, y tengo docenas de pruebas y varias bibliotecas simuladas. Sin embargo, tengo algunas dudas sobre si está bien crear un objeto simulado global. Pregunté esto en una pregunta separada y sigo esperando respuestas. Además de esto estoy contento con la solución.


ACTUALIZAR: El problema del objeto global se puede remediar fácilmente creando el objeto, por ejemplo, en el constructor del accesorio de prueba, y simplemente almacenando un puntero a ese objeto en una variable global.

Sin embargo, también tenga en cuenta mi respuesta alternativa a esta pregunta, que acabo de publicar.

  • Seguí tu procedimiento. La única diferencia es que no hay ningún parámetro en su función dada. Por el contrario, dos parámetros en mi función (uno es struct y otro es int). en interfaz, virtual DltReturnValue dlt_client_connect(DltClient *client, int verbose) = 0; y en el archivo de prueba TEST_F(DltLogClient_test, init) { DltClient *client = new DltClient(); EXPECT_CALL(MockXIFObj, dlt_client_init(client, 0)); EXPECT_EQ(0, dltLogclient->init()); } Obtengo un SEGFAULT. ¿Dónde me equivoqué?

    – Nibir

    6 de marzo de 2018 a las 8:54


  • Esto falla porque el objeto simulado se destruye después de realizar las pruebas, pero debe destruirse como parte de la prueba. Así que la solución de matthandi es correcta, y esta no lo es.

    –Daid Braam

    18 de marzo de 2020 a las 9:38

  • Si esto provoca un bloqueo, entonces tiene algo que ver con su código. Pero como escribí en la edición en la parte inferior, puede dejar que el objeto simulado se construya y destruya fácilmente junto con el dispositivo de prueba. Eso debería resolver tu problema.

    – Jorge P.

    18 de marzo de 2020 a las 19:21

Esta es otra respuesta mía a esta pregunta. En los dos años que pasaron desde la primera respuesta, llegué a comprender que GMock es simplemente el marco incorrecto para burlarse de las funciones de C. En situaciones en las que tiene muchas funciones para burlarse, mi respuesta publicada anteriormente es simplemente demasiado engorrosa. La razón es que GMock usa Costuras de objetos para reemplazar el código de producción con código simulado. Esto se basa en clases polimórficas, que no existen en C.

En cambio, para burlarse de las funciones de C, debe usar Enlace costuras, que reemplazan el código de producción con el código simulado en el momento del enlace. Existen varios marcos para este propósito, pero mi favorito es el Marco de funciones falsas (FFF). Compruébalo, es mucho más simple que GMock. También funciona perfectamente bien en aplicaciones C++.

Para los interesados, aquí hay un buen artículo por Michael Feathers sobre los diferentes tipos de costura.

Llevaba mucho tiempo buscando una solución para simular las funciones c heredadas con googleMock sin cambiar el código existente y los últimos días encontré el siguiente artículo realmente excelente: https://www.codeproject.com/articles/1040972/using-googletest-and-googlemock-frameworks-for-emb

Hoy escribí mi primera prueba de unidad para funciones c usando gmock y tomé como ejemplo dos funciones de la biblioteca bcm2835.c (http://www.airspayce.com/mikem/bcm2835/) para la programación de raspberry Pi: Aquí está mi solución: estoy usando gcc 4.8.3. bajo Eclipse y Windows. Tenga cuidado de configurar la opción del compilador -std=gnu++11.

Aquí están mis funciones para ser probadas.

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

Incluye y define para pruebas unitarias con googleTest / googleMock

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

Funciones simuladas de 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));
};

Crear un dispositivo de prueba

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

Crear instancias de funciones lib simuladas

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

Funciones lib falsas para conectar Mocks con las funciones 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);}

Cree una clase de prueba unitaria para BCM2835 desde TestFixture

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

Escriba las pruebas usando googleTest y 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);
}

Resultados 🙂

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

Espero que ayude 🙂 – para mí, esta es una solución realmente funcional.

  • ¿Dónde vive el código que mostró para “Crear instancias de funciones lib simuladas”? ¿En qué archivo .cpp está eso?

    – bjornruffians

    15 mayo 2018 a las 19:27


Puedes usar el Chica biblioteca para simular la función C estilo GoogleMock.
Hay una muestra completa en el repositorio, pero solo una muestra:

INSTALL_MOCK(close);
CUTIE_EXPECT_CALL(fclose, _).WillOnce(Return(i));

Tuve un caso similar en un proyecto que estaba probando unitariamente. Mi solución fue crear dos archivos make, uno para producción y otro para prueba.

Si la función func_1() se define en el encabezado ah y se implementa en a.cpp, entonces, para realizar pruebas, puede agregar un nuevo archivo fuente a_testing.cpp, que implementará todas las funciones en ah como código auxiliar. Para pruebas unitarias, simplemente compile y vincule con a_testing.cpp en lugar de a.cpp y el código probado llamará a su código auxiliar.

En a_testing.cpp, puede reenviar la llamada a un objeto gmock que establecerá expectativas y acciones como de costumbre en función del estado y los parámetros.

Sé que no es perfecto, pero funciona y resuelve el problema sin cambiar el código de producción o las interfaces en absoluto.

En cada UT estamos tratando de verificar un comportamiento específico.

Debes fingir algo cuando es muy difícil/imposible (necesitamos aislar nuestra unidad)/pasar mucho tiempo (tiempo de ejecución…) para simular un comportamiento específico.

El uso de una función ‘C’ de manera explícita significa que la función es parte de su unidad (por lo tanto, no debe burlarse de ella). En esta respuesta explico la iniciativa de probar el método tal cual (en la edición…). en mi opinion deberias llamar func con los parámetros que causan func_1 para simular el comportamiento que desea verificar.

GMock se basa en una compilación falsa (macros), por lo tanto, no puede hacer tal cosa. Para falsificar los métodos ‘C’, debe usar herramientas diferentes, como Aislador Typemock++.

Si no quieres usar Isolator++, entonces deberías refactorizar tu método; Cambiar func para func(int a, <your pointer the function>) y luego use el puntero en lugar de func_1.

Mi cuadro en esta respuesta podría ayudar a decidir la forma de manejar su caso.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad